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;
+}
diff --git a/std/player/channel.c b/std/player/channel.c
new file mode 100644
index 0000000..9f8512a
--- /dev/null
+++ b/std/player/channel.c
@@ -0,0 +1,579 @@
+// MorgenGrauen MUDlib
+//
+// channel.c -- channel client
+//
+// $Id: channel.c 9404 2015-12-13 00:21:44Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <util.h>
+#include <thing/properties.h>
+#include <living/comm.h>
+#include <player.h>
+#include <player/comm.h>
+#include <daemon.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+#include <wizlevels.h>
+#include <defines.h>
+#include <properties.h>
+#include <sys_debug.h>
+#include <regexp.h>
+
+#define P_SWAP_CHANNELS "swap_channels"
+#define P_CHANNEL_SHORT "short_channels"
+
+#define CHANNELCMDS "[#@%$&()<>a-zA-Z0-9\\-]"
+
+#define DEFAULT_CHANNELS ({"Abenteuer", "Anfaenger","Grats","Tod", "ZT"})
+#define DEFAULT_SHORTCUTS \
+([ \
+ "b":"Abenteuer", \
+ "a":"Allgemein", \
+ "B":"Beileid", \
+ "q":"D-chat", \
+ "G":"Grats", \
+ "M":"Moerder", \
+ "h":"Seher", \
+ "T":"Tod", \
+])
+
+#define WIZARD_SHORTCUTS \
+([ \
+ "P":"D-code", \
+ "D":"Debug", \
+ "O":"Intercode", \
+ "I":"Intermud", \
+ "m":"Magier", \
+])
+
+
+private nosave mapping shortcut;
+private nosave int c_status;
+
+void create()
+{
+ Set(P_CHANNELS, SAVE, F_MODE);
+ Set(P_CHANNELS, DEFAULT_CHANNELS);
+ Set(P_SWAP_CHANNELS, SAVE, F_MODE);
+ Set(P_STD_CHANNEL, "Allgemein");
+ Set(P_STD_CHANNEL, SAVE, F_MODE);
+ Set(P_CHANNEL_SHORT, SAVE, F_MODE);
+ Set(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
+ + (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
+}
+
+static mixed _query_localcmds()
+{
+ return ({({"-","ChannelParser", 1, 0}),
+ ({"ebene", "ChannelAdmin", 0, 0}),
+ ({"ebenen", "ChannelAdmin", 1, 0}),
+ });
+}
+
+mixed RegisterChannels()
+{
+ mixed err;
+ if(extern_call() &&
+ previous_object() != find_object(CHMASTER)) return;
+ c_status = 0;
+ shortcut = QueryProp(P_CHANNEL_SHORT);
+ SetProp(P_CHANNELS, map(QueryProp(P_CHANNELS) || ({}),
+ #'lower_case));
+ err = filter(QueryProp(P_CHANNELS),
+ symbol_function("join", CHMASTER),
+ this_object());
+ if(QueryProp(P_LEVEL) < 5) return err;
+ while(sizeof(err)) {
+ CHMASTER->new(err[0], this_object());
+ err[0..0] = ({});
+ }
+ return err;
+}
+
+mixed RemoveChannels()
+{
+ closure cl;
+ mixed err=({});
+ if(extern_call() &&
+ previous_object() != find_object(CHMASTER)) return;
+ if(!c_status) c_status = 1;
+ else return ({});
+ cl=symbol_function("leave", CHMASTER);
+ if (closurep(cl)) {
+ err = filter(QueryProp(P_CHANNELS), cl, this_object());
+ SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - err);
+ }
+ return err;
+}
+
+varargs private string getName(mixed x, int fall) {
+
+ mixed o = closurep(x) ? query_closure_object(x) : x;
+ if(stringp(o) && sizeof(o) && (x = find_object(o)))
+ o = x;
+
+ // Objekte
+ if (objectp(o)) {
+ // Magier sehen unsichtbare nicht nur als "Jemand"
+ if (o->QueryProp(P_INVIS) && IS_LEARNING(this_object()))
+ return "("+capitalize(getuid(o))+")";
+ // Froesche mit Namen versorgen.
+ if (o->QueryProp(P_FROG))
+ return "Frosch "+capitalize(getuid(o));
+ // Default (Unsichtbare als "Jemand" (s. Name()))
+ return o->Name(fall, 2)||"<Unbekannt>";
+ }
+ // Strings
+ else if (stringp(o) && sizeof(o)) {
+ if (o[0] == '/') {
+ // unsichtbare Objekte...
+ int p = strstr(o, "$");
+ if (p != -1) {
+ // Magier im Magiermodus kriegen den Realnamen, andere nicht.
+ if (IS_LEARNING(this_object()))
+ return o[1..p-1];
+ else
+ return o[p+1..];
+ }
+ else
+ // doch nicht unsichtbar
+ return (fall == WESSEN ? o+"s" : o);
+ }
+ else
+ // nicht unsichtbar
+ return (fall == WESSEN ? o+"s" : o);
+ }
+ // Fall-through
+ return "<Unbekannt>";
+}
+
+// <nonint> unterdrueckt die AUsgabe an den Spieler und liefert den Text
+// zurueck. Wird nur fuer die Ebenenhistory benutzt.
+string ChannelMessage(mixed* msg, int nonint)
+{
+ string channel_message;
+ string channel=msg[0];
+ object sender=msg[1];
+ string message=msg[2];
+ int msg_type = msg[3];
+
+ if ( previous_object() != find_object(CHMASTER) &&
+ previous_object() != ME )
+ return 0;
+
+ string sender_name = getName(sender, msg_type == MSG_GEMOTE ? WESSEN : WER);
+ int prepend_indent_flag=QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0;
+ switch(msg_type) {
+ case MSG_EMPTY:
+ channel_message= message+"\n";
+ break;
+ case MSG_GEMOTE:
+ case MSG_EMOTE:
+ channel_message = break_string(sender_name + " "+ message+"]",
+ 78, sprintf("[%s:", channel),
+ BS_INDENT_ONCE|prepend_indent_flag);
+ break;
+ case MSG_SAY:
+ default:
+ string presay=sprintf("[%s:%s] ", channel, sender_name);
+ channel_message = break_string(message, max(78,sizeof(presay)+10),
+ presay, prepend_indent_flag);
+ break;
+ }
+ if(nonint)
+ return channel_message;
+
+ // Wenn GMCP sich um Uebertragung der Nachricht kuemmert, wird ReceiveMsg()
+ // nicht mehr aufgerufen.
+ if (GMCP_Channel(channel_message, channel, sender_name) != 1)
+ {
+ // Der Ebenenname muss in Kleinbuchstaben uebergeben werden, damit die
+ // Ignorierepruefung funktioniert. Die ignorierestrings sind naemlich alle
+ // kleingeschrieben.
+ ReceiveMsg(channel_message,
+ MT_COMM|MT_FAR|MSG_DONT_STORE|MSG_DONT_WRAP,
+ MA_CHANNEL"." + lower_case(channel), 0, sender);
+ }
+ return 0;
+}
+
+private void createList(string n, mixed a, mixed m, mixed l)
+{
+ int pos; string sh, *mem;
+ if((pos = member(map(m_values(shortcut), #'lower_case/*'*/), n)) != -1)
+ sh = m_indices(shortcut)[pos];
+ else sh = "";
+ mem=map(a[I_MEMBER],#'getName/*'*/, WER);
+ mem-=({"<MasteR>"});
+ l += ({ sprintf("%-12.12'.'s %c[%-1.1s] %|12.12' 's (%-|3' 'd) %-42.42s\n",
+ a[I_NAME], (member(m, n) != -1 ? '*' : ' '), sh,
+ a[I_MASTER] ?
+ getName(a[I_MASTER]) : getName(a[I_ACCESS]),
+ sizeof(mem),
+ (closurep(a[I_INFO]) && objectp(query_closure_object(a[I_INFO]))) ?
+ funcall(a[I_INFO]) || "- Keine Beschreibung -" :
+ (stringp(a[I_INFO]) ? a[I_INFO] : "- Keine Beschreibung -")
+ ) });
+}
+
+private mixed getChannel(string ch)
+{
+ mixed ff;
+ if(!sizeof(ch)) ch = QueryProp(P_STD_CHANNEL);
+ if(shortcut && shortcut[ch]) ch = shortcut[ch];
+ return CHMASTER->find(ch, this_object());
+}
+
+#ifndef DEBUG
+#define DEBUG(x) if (funcall(symbol_function('find_player),"zesstra"))\
+ tell_object(funcall(symbol_function('find_player),"zesstra"),\
+ "MDBG: "+x+"\n")
+#endif
+int ChannelParser(string args)
+{
+ mixed ch, cmd, tmp;
+ int pos, type, err;
+ string txt;
+ cmd = query_verb();
+ args = _unparsed_args();
+ notify_fail("Benutzung: -<Ebene>[ ]['|:|;]<Text>\n"
+ " -<Ebene>[+|-|?|!|*]\n"
+ " -?\n");
+ if(!cmd && !args) return 0;
+ if(!args) args = "";
+ cmd = cmd[1..];
+ if(sizeof(cmd = regexplode(cmd,
+ "^" CHANNELCMDS "*"
+ "([+-]|\\!|\\?|\\*)*")) > 1)
+ {
+ //z.B. cmd= ({"","allgemein",":testet"})
+ if(sizeof(cmd[1]) > 1 &&
+ strstr("+-?!*", cmd[1][<1..<1]) > -1)
+ tmp = cmd[1][0..<2];
+ else
+ tmp = cmd[1];
+ if(cmd[1] != "?" && cmd[1] != "!")
+ if(pointerp(ch = getChannel(tmp)))
+ {
+ notify_fail("Diese Angabe war nicht eindeutig! "
+ "Folgende Ebenen passen:\n"
+ +implode(ch, ", ")+"\n");
+ return 0;
+ }
+ else if(!ch) return (notify_fail("Die Ebene '"+tmp
+ +"' gibt es nicht!\n"), 0);
+ //DEBUG(sprintf("ChanCmd: %O\n",cmd));
+ if (sizeof(cmd[1])) {
+ switch(cmd[1][<1]) {
+ case '+':
+ switch(CHMASTER->join(ch, this_object()))
+ {
+ case E_ACCESS_DENIED:
+ notify_fail("Du darfst an die Ebene '"+ch+"' nicht heran.\n");
+ return 0;
+ case E_ALREADY_JOINED:
+ notify_fail("Du hast diese Ebene schon betreten!\n");
+ return 0;
+ default: break;
+ }
+ write("Du betrittst die Ebene '"+ch+"'.\n");
+ if(member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
+ SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
+ return 1;
+ case '-':
+ switch(CHMASTER->leave(ch, this_object()))
+ {
+ case E_ACCESS_DENIED:
+ write("Du kannst die Ebene '"+ch+"' nicht verlassen.\n");
+ break;
+ case E_NOT_MEMBER:
+ write("Wie willst Du eine Ebene verlassen, welche Du nicht "
+ "betreten hast?\n");
+ break;
+ default:
+ write("Du verlaesst die Ebene '"+ch+"'.\n");
+ SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - ({ lower_case(ch), ch }));
+ break;
+ }
+ return 1;
+ case '!':
+ case '?':
+ {
+ mapping l;
+ if(mappingp(l = CHMASTER->list(this_object())))
+ if(stringp(ch) && sizeof(ch) && pointerp(l[ch = lower_case(ch)]))
+ {
+ int c; object o; string n; string *m;
+ m=sort_array(map(l[ch][I_MEMBER],#'getName/*'*/, WER),#'>/*'*/);
+ m-=({"<MasteR>"});
+ write(l[ch][I_NAME]+", "+funcall(l[ch][I_INFO])+".\n");
+ write("Du siehst "+((c = sizeof(m)) > 0
+ ? (c == 1 ? "ein Gesicht" : c+" Gesichter")
+ : "niemanden")+" auf der Ebene '"
+ +l[ch][I_NAME]+"':\n");
+ write(break_string(implode(m,", "), 78));
+ write((l[ch][I_MASTER] ?
+ getName(l[ch][I_MASTER]) : getName(l[ch][I_ACCESS], WER))
+ +" hat das Sagen auf dieser Ebene.\n");
+ }
+ else
+ {
+ mixed list; list = ({});
+ if(cmd[1][<1] == '!')
+ l -= mkmapping(m_indices(l) - QueryProp(P_CHANNELS));
+ walk_mapping(l, #'createList/*'*/, QueryProp(P_CHANNELS), &list);
+ list = sort_array(list, #'>/*'*/);
+ txt = sprintf("%-12.12' 's [A] %|12' 's (%-3' 's) %-42.42s\n",
+ "Name", "Eigner", "Sp", "Beschreibung")
+ + "-------------------------------------------------------"
+ + "-----------------------\n"
+ + implode(list, "");
+ More(txt);
+ }
+ return 1;
+ }
+ case '*':
+ {
+ mixed hist; int amount;
+ if(!pointerp(hist = CHMASTER->history(ch, this_object()))
+ || !sizeof(hist))
+ {
+ write("Es ist keine Geschichte fuer '"+ch+"' verfuegbar.\n");
+ return 1;
+ }
+
+ //(Zesstra) cmd hat offenbar immer 3 Elemente...
+ //bei -all* ({"","all*",""})
+ //bei -all*10 ({"","all*,"10"})
+ //also ist bei -all* amount immer == 0 und es funktioniert eher zufaellig.
+ /*if(sizeof(cmd) > 2)
+ amount = to_int(cmd[2]);
+ else
+ amount=sizeof(hist);*/
+ amount=to_int(cmd[2]);
+ if (amount <= 0 || amount >= sizeof(hist))
+ amount=sizeof(hist);
+
+ txt = "Folgendes ist auf '"+ch+"' passiert:\n"
+ + implode(map(hist[<amount..], #'ChannelMessage/*'*/, 1), "");
+ More(txt);
+ return 1;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ if(sizeof(cmd = implode(cmd[2..], "")))
+ args = cmd + (sizeof(args) ? " " : "") + args;
+
+ // KOntrollchars ausfiltern.
+ args = regreplace(args,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
+ if(!sizeof(args)) return 0;
+
+ //Wenn cmd leer ist: MSG_SAY
+ if (!sizeof(cmd)) type=MSG_SAY;
+ else {
+ switch(cmd[0])
+ {
+ case ':' :
+ type = MSG_EMOTE;
+ args = args[1..];
+ break;
+ case ';' :
+ type = MSG_GEMOTE;
+ args = args[1..];
+ break;
+ case '\'':
+ args = args[1..];
+ default : type = MSG_SAY; break;
+ }
+ }
+ if(!ch || !sizeof(ch)) ch = QueryProp(P_STD_CHANNEL);
+ if((err = CHMASTER->send(ch, this_object(), args, type)) < 0)
+ if(!(err = CHMASTER->join(ch, this_object())))
+ {
+ if(member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
+ SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
+ err = CHMASTER->send(ch, this_object(), args, type);
+ }
+
+ switch(err)
+ {
+ case E_ACCESS_DENIED:
+ notify_fail("Auf der Ebene '"+ch+"' darfst Du nichts sagen.\n");
+ return 0;
+ case E_NOT_MEMBER:
+ notify_fail("Du hast die Ebene '"+ch+"' nicht betreten!\n");
+ return 0;
+ }
+ return 1;
+}
+
+int ChannelAdmin(string args)
+{
+ string n, descr, sh, cn;
+ mixed pa, tmp;
+ args = _unparsed_args();
+ notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
+ " ebene <Abkuerzung>=\n"
+ " ebene abkuerzungen [standard]\n"
+ " ebene standard <Ebene>\n"
+ " ebene an|ein|aus\n"
+ +(QueryProp(P_LEVEL) >= 5 ?
+ " ebene neu <Name> <Bezeichnung>\n"
+ " ebene beschreibung <Name> <Beschreibung>\n" : "")
+ +(IS_ARCH(this_object()) ?
+ " ebene kill <Name>\n"
+ " ebene clear <Name>\n": ""));
+ if(!args || !sizeof(args)) return 0;
+ if(sscanf(args, "kill %s", n) && IS_ARCH(this_object()))
+ {
+ if(!(cn = CHMASTER->find(n, this_object()))) cn = n;
+ switch(CHMASTER->remove(cn, this_object()))
+ {
+ case E_ACCESS_DENIED:
+ notify_fail("Die Ebene '"+cn+"' lies sich nicht entfernen!\n");
+ return 0;
+ }
+ write("Du entfernst die Ebene '"+cn+"'.\n");
+ return 1;
+ }
+ if(sscanf(args, "clear %s", n) && IS_ARCH(this_object()))
+ {
+ if(!(cn = CHMASTER->find(n, this_object()))) cn = n;
+ switch(CHMASTER->clear_history(cn, this_object()))
+ {
+ case E_ACCESS_DENIED:
+ notify_fail("Der Verlauf zur Ebene '"+cn+"' lies sich nicht entfernen!\n");
+ return 0;
+ }
+ write("Du entfernst den Verlauf zur Ebene '"+cn+"'.\n");
+ return 1;
+ }
+ if(sscanf(args, "neu %s %s", n, descr) == 2)
+ {
+ mixed x;
+ if(QueryProp(P_LEVEL) < 5)
+ return (notify_fail("Neue Ebenen zu erstellen ist dir verwehrt.\n"), 0);
+ if(!sizeof(regexp(({ n }), "^" CHANNELCMDS CHANNELCMDS "*")))
+ return (notify_fail("Der Name '"+n+"' ist nicht konform!\n"), 0);
+ if (sizeof(n) > 20 )
+ return(notify_fail("Der Name '"+n+"' ist zu lang.\n"), 0);
+ switch(x = CHMASTER->new(n, this_object(), descr))
+ {
+ case E_ACCESS_DENIED:
+ notify_fail("Diese Ebene darfst du nicht erschaffen!\n"); break;
+ default:
+ write("Du erschaffst die Ebene '"+n+"'.\n");
+ SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ lower_case(n) }));
+ return 1;
+ }
+ }
+ if(sscanf(args, "beschreibung %s %s", n, descr) == 2)
+ {
+ mixed ch;
+ cn = CHMASTER->find(n, this_object());
+ if(!cn || pointerp(cn))
+ return (notify_fail("Die Ebene '"+n+"' existiert nicht oder die Angabe "
+ "war nicht eindeutig.\n"), 0);
+ ch = CHMASTER->list(this_object());
+ if(ch[lower_case(cn)][I_MASTER] != this_object())
+ return (notify_fail("Du bist nicht berechtigt die Beschreibung der Ebene"
+ " '"+cn+"' zu aendern.\n"), 0);
+ ch[lower_case(cn)][I_INFO] = descr;
+ write("Die Ebene '"+cn+"' hat ab sofort die Beschreibung:\n"+descr+"\n");
+ return 1;
+ }
+ if(sscanf(args, "%s=%s", sh, n) == 2 && sizeof(n))
+ {
+ mapping sc;
+ if(pointerp(tmp = CHMASTER->find(n, this_object())) || !tmp)
+ return (notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
+ +(pointerp(tmp) ? implode(tmp, ", ") + "\n" :
+ "Ebene '"+n+"' nicht gefunden!\n")), 0);
+ sc = QueryProp(P_CHANNEL_SHORT);
+ if(!sc) sc = ([]);
+ sc[sh] = tmp;
+ SetProp(P_CHANNEL_SHORT, sc);
+ shortcut = QueryProp(P_CHANNEL_SHORT);
+ write("'"+sh+"' wird jetzt als Abkuerzung fuer '"+tmp+"' anerkannt.\n");
+ return 1;
+ }
+ if(sscanf(args, "%s=", sh))
+ {
+ SetProp(P_CHANNEL_SHORT, m_copy_delete(QueryProp(P_CHANNEL_SHORT) || ([]), sh));
+ shortcut = QueryProp(P_CHANNEL_SHORT);
+ write("Du loeschst die Abkuerzung '"+sh+"'.\n");
+ return 1;
+ }
+ if(args == "an" || args == "ein")
+ {
+ mixed excl;
+ if(pointerp(QueryProp(P_SWAP_CHANNELS)))
+ SetProp(P_CHANNELS, QueryProp(P_SWAP_CHANNELS));
+ else
+ SetProp(P_CHANNELS, m_indices(CHMASTER->list(this_object())));
+ excl = RegisterChannels();
+ write("Du schaltest folgende Ebenen ein:\n"
+ +break_string(implode(QueryProp(P_CHANNELS) - excl, ", "), 78));
+ SetProp(P_SWAP_CHANNELS, 0);
+ return 1;
+ }
+ if(args == "aus")
+ {
+ SetProp(P_SWAP_CHANNELS, QueryProp(P_CHANNELS));
+ RemoveChannels();
+ SetProp(P_CHANNELS, ({}));
+ write("Du stellst die Ebenen ab.\n");
+ return 1;
+ }
+ pa = old_explode(args, " ");
+ if(!strstr("abkuerzungen", pa[0]))
+ {
+ string txt; txt = "";
+ if(sizeof(pa) > 1 && !strstr("standard", pa[1]))
+ {
+ write("Die Standard Abkuerzungen werden gesetzt.\n");
+ SetProp(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
+ + (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
+ }
+ walk_mapping(QueryProp(P_CHANNEL_SHORT),
+ lambda(({'i/*'*/, 'c, 'r}),
+ ({#'+=, 'r/*'*/,
+ ({#'sprintf/*'*/, "%5.5s = %s\n", 'i, 'c})})),
+ &txt);
+ txt = sprintf("Folgende Abkuerzungen sind definiert:\n%-78#s\n",
+ implode(sort_array(old_explode(txt, "\n"), #'>/*'*/), "\n"));
+ More(txt);
+ return 1;
+ }
+ if(!strstr("standard", pa[0]))
+ if(sizeof(pa) < 2)
+ return (notify_fail("Benutzung: ebene standard <Ebene>\n"
+ +(QueryProp(P_STD_CHANNEL)
+ ? "Momentan ist '"+QueryProp(P_STD_CHANNEL)
+ +"' eingestellt.\n"
+ : "Es ist keine Standardebene eingestellt.\n")),0);
+ else
+ if(pointerp(tmp = CHMASTER->find(pa[1], this_object())))
+ return (notify_fail("Das war keine eindeutige Angabe! "
+ "Folgende Ebenen passen:\n"
+ +break_string(implode(tmp, ", "), 78)), 0);
+ else
+ if(!tmp) return (notify_fail("Ebene '"+pa[1]+"' nicht gefunden!\n"),0);
+ else
+ {
+ write("'"+tmp+"' ist jetzt die Standardebene.\n");
+ SetProp(P_STD_CHANNEL, tmp);
+ return 1;
+ }
+ return(0);
+}
+
diff --git a/std/player/combat.c b/std/player/combat.c
new file mode 100644
index 0000000..abb5a1b
--- /dev/null
+++ b/std/player/combat.c
@@ -0,0 +1,217 @@
+// MorgenGrauen MUDlib
+//
+// player/combat.c -- combat statistics
+//
+// $Id: combat.c 9008 2015-01-06 17:20:17Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/combat";
+inherit "/std/player/pklog";
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <combat.h>
+#include <new_skills.h>
+
+#define ME this_object()
+#define STATMASTER "/p/service/rochus/guildstat/master"
+
+private nosave closure mod_def_stat;
+private nosave string *plAttacked = ({});
+
+protected void create() {
+ combat::create();
+ // P_NO_ATTACK ist nicht fuer Spieler gedacht. Ausnahme: Spieler ist
+ // Geist, dann setzt das Spielerobjekt aber selber.
+ Set(P_NO_ATTACK, SECURED|NOSETMETHOD, F_MODE_AS);
+
+ Set(P_HELPER_NPC, PROTECTED, F_MODE);
+ SetProp(P_HELPER_NPC, ([]) );
+}
+
+// Maske fuer alle moeglichen Klassen von Helfer-NPC
+#define CLASS_MASK 0x1fffffff
+/** registriert den NPC als Helfer von diesem Spieler.
+ @param[in] npc object Helfer-NPC
+ @param[in] flags int Bitfeld von Flags
+ @return int 1, falls der Helfer-NPC registriert wurde und noch nicht
+ registriert war.
+ @attention Nutzt aus, dass QueryProp(P_HELPER_NPC) _keine_ Kopie des
+ Mappings in der Prop liefert.
+ */
+public int RegisterHelperNPC(object npc, int flags) {
+ if (!objectp(npc))
+ raise_error(sprintf( "Wrong argument 1 in RegisterHelperNPC(). "
+ "Expected <object>, got %.100O\n", npc));
+ if (!intp(flags) || flags < 1)
+ raise_error(sprintf( "Wrong argument 2 in RegisterHelperNPC(). "
+ "Expected positive <int>, got %O\n", flags));
+
+ mapping helpers = QueryProp(P_HELPER_NPC);
+
+ // schon registrierte sind witzlos.
+ if (member(helpers, npc))
+ return 0;
+
+ // auf exklusive Helfer pruefen.
+ foreach(object helper, int fl: helpers) {
+ // flags identisch? Dann Klasse und Exklusivitaet identisch
+ if (fl == flags)
+ return 0;
+ // oder einer von beiden exklusiv und beide in der gleichen Klasse?
+ else if ( ((fl & EXCLUSIVE_HELPER) || (flags & EXCLUSIVE_HELPER))
+ && ((fl & CLASS_MASK) == (flags & CLASS_MASK)) )
+ return 0;
+ }
+ // scheint wohl ok zu sein. Registrieren und Prop im NPC setzen.
+ helpers += ([ npc: flags ]);
+ npc->SetProp(P_HELPER_NPC, ({ this_object(), flags }) );
+ // momentan unnoetig, da helpers keine Kopie ist.
+ // SetProp(P_HELPER_NPC, helpers);
+
+ return 1;
+}
+#undef CLASS_MASK
+
+/** de-registriert den NPC als Helfer von diesem Spieler.
+ @param[in] npc object Helfer-NPC
+ @return int 1, falls der Helfer-NPC registriert war und entfernt wurde.
+ @attention Nutzt aus, dass QueryProp(P_HELPER_NPC) _keine_ Kopie des
+ Mappings in der Prop liefert.
+ */
+public int UnregisterHelperNPC(object npc) {
+ if (!objectp(npc))
+ raise_error(sprintf("Wrong argument in UnregisterHelpernNPC(). "
+ "Expected <object>, got %.100O\n", npc));
+
+ mapping helpers = QueryProp(P_HELPER_NPC);
+ if (member(helpers, npc)) {
+ m_delete(helpers, npc);
+ // momentan unnoetig, da helpers keine Kopie ist.
+ // SetProp(P_HELPER_NPC, helpers);
+ npc->SetProp(P_HELPER_NPC, 0);
+ return 1;
+ }
+ return 0;
+}
+
+/** Feind eintragen.
+ * Traegt ob als Feind ein. Dies allerdings nur, wenn ob kein Spieler ist
+ * oder beide Spieler (dieses Objekt und ob) in der Schattenwelt sind oder
+ * beide Spieler Testspieler sind.
+ @param[in] ob potentieller Feind.
+ @return int 1, falls ob als _neuer_ Feind eingetragen wurde.
+ */
+public int InsertEnemy(object ob) {
+ // wenn ob ein Spieler ist und nicht sowohl ich als auch ob in der
+ // Schattenwelt sind, wird ob nicht als Feind eingetragen.
+ if (query_once_interactive(ob)
+ && (strstr(object_name(environment(ob)),"/d/schattenwelt/")!=0
+ || strstr(object_name(environment(ME)),"/d/schattenwelt/")!=0)
+ && (!QueryProp(P_TESTPLAYER) || !ob->QueryProp(P_TESTPLAYER))
+ )
+ {
+ return 0;
+ }
+ return ::InsertEnemy(ob);
+}
+
+/** Hat dieser Spieler den Spieler pl angegriffen?.
+ @param[in] pl object zu pruefender Spieler
+ @return int 1, falls dieser Spieler pl angegriffen hat.
+ @attention Nebeneffekt: bereinigt den internen Speicher von UIDs von
+ zerstoerten Spielern und solchen, die keine Feinde mehr sind.
+ */
+public int QueryPlAttacked(object pl) {
+ object ob;
+
+ if ( !objectp(pl) )
+ return 0;
+
+ foreach(string plname: plAttacked) {
+ if ( !( ob=(find_player(plname)||find_netdead(plname)) )
+ || ( !IsEnemy(ob) && !(ob->IsEnemy(ME)) ) )
+ plAttacked -= ({plname}); // ja, das geht. ;-)
+ }
+ return (member( plAttacked, getuid(pl) ) >= 0);
+}
+
+/** kill - Kampf starten.
+ * Fuegt ob der Feindesliste hinzu.
+ */
+public int Kill(object ob) {
+
+ if (!objectp(ob)) return 0;
+
+ // dies dient nur dazu, das plAttacked mapping zu bereinigen.
+ // TODO: besser machen. ;-)
+ if ( query_once_interactive(ob) && !IsEnemy(ob)) {
+ QueryPlAttacked(ME);
+ ob->QueryPlAttacked(ob); // aktualisieren ...
+ }
+
+ int res = combat::Kill(ob);
+
+ // falls ob nen Spieler ist, pruefen, ob es ein Spieler-Spieler-Angriff
+ // ist.
+ // Dabei ggf. loggen und Magier verstaendigen.
+ if (query_once_interactive(ob) && CheckPlayerAttack(ME, ob, 0))
+ {
+ if (res == -4) // feind wurde nicht eingetragen
+ tell_object(ME, "Ein goettlicher Befehl hindert Dich am Kampf.\n");
+ else
+ plAttacked += ({ getuid(ob) });
+ }
+
+ return res;
+}
+
+public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy) {
+ int delta_hp,res;
+
+ if (query_once_interactive(ME)
+ && !IS_LEARNER(ME)
+ && !objectp(get_type_info(mod_def_stat,2))) {
+ object ma;
+ if (!objectp(ma=find_object(STATMASTER)))
+ return ::Defend(dam,dam_type,spell,enemy);
+ // Statistik nur aufrufen falls Master geladen
+ mod_def_stat=symbol_function("ModifyDefendStat",ma);
+ }
+
+ if (closurep(mod_def_stat))
+ delta_hp=QueryProp(P_HP);
+
+ res=::Defend(dam,dam_type,spell,enemy);
+
+ if (closurep(mod_def_stat)) {
+ delta_hp-=QueryProp(P_HP);
+ if (delta_hp<0)
+ delta_hp=0;
+ funcall(mod_def_stat,
+ QueryProp(P_GUILD),
+ QueryProp(P_GUILD_LEVEL),
+ dam-10*delta_hp,
+ dam_type,
+ spell);
+ }
+
+ return res;
+}
+
+// Spieler koennen als Geist nicht kämpfen
+// TODO: pruefen, ob das Setzen und Loeschen der Prop in set_ghost() nicht
+// auch ausreichen wuerde. In dem Fall muesste man aber P_NO_ATTACK auch
+// speichern, da P_GHOST gespeichert wird...
+static mixed _query_no_attack()
+{
+ if ( QueryProp(P_GHOST) )
+ return 1;
+
+ return Query(P_NO_ATTACK);
+}
diff --git a/std/player/comm.c b/std/player/comm.c
new file mode 100644
index 0000000..f91f1de
--- /dev/null
+++ b/std/player/comm.c
@@ -0,0 +1,1922 @@
+// MorgenGrauen MUDlib
+//
+// player/comm.c-- basic player communiction commands
+//
+// $Id: comm.c 9576 2016-06-18 15:00:01Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+//#pragma range_check
+
+inherit "/std/living/comm";
+inherit "/std/player/channel";
+inherit "/std/player/comm_structs";
+
+#include <input_to.h>
+
+#define NEED_PROTOTYPES
+#include <player/quest.h>
+#include <player/gmcp.h>
+#include <living/description.h>
+#undef NEED_PROTOTYPES
+
+#include <sys_debug.h>
+
+#include <thing/properties.h>
+#include <player/comm.h>
+#include <player/base.h>
+
+#include <properties.h>
+#include <config.h>
+#include <ansi.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <udp.h>
+#include <defines.h>
+#include <daemon.h>
+#include <strings.h>
+#include <regexp.h>
+#include <interactive_info.h>
+
+#define TELLHIST_DISABLED 0
+#define TELLHIST_NO_MESSAGE 1
+#define TELLHIST_ENABLED 2
+#define TELLHIST_LONGLIFE 3
+
+#define ECHO_COST 50
+#define ERWIDER_PARAM ","
+
+#define ZDEBUG(x) if (find_player("zesstra"))\
+ efun::tell_object(find_player("zesstra"),"CommDBG: "+x+"\n")
+
+private int tell_history_enabled = TELLHIST_NO_MESSAGE;
+private nosave mapping tell_history=([]);
+private nosave string *commreceivers = ({});
+private nosave string last_comm_partner;
+private nosave int last_beep_time;
+
+// Statusreporte aktiviert? Binaere Flags (s. set_report())
+private int stat_reports;
+// interner Cache fuer die LP/KP/Gift-Werte fuer die Statusreport-Ausgaben
+// Eintraege (in dieser Reihenfolge): P_HP, P_SP, Giftstatus
+// Initialisierung erfolgt beim ersten Report nach Login
+private nosave mixed *report_cache;
+
+// Puffer fuer Kobold.
+private nosave struct msg_buffer_s kobold = (<msg_buffer_s>
+ buf: allocate(32),
+ index: -1,);
+#define MAX_KOBOLD_LIMIT 256
+
+varargs string name(int casus, int demonst);
+
+//local property prototypes
+static int _query_intermud();
+public int RemoveIgnore(string ign);
+public int AddIgnore(string ign);
+
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+ string msg_prefix, object origin);
+
+// erzeugt sortierte Liste an Kommunikationspartnern
+private string *sorted_commpartners(int reversed);
+
+private nosave string *buffer = ({});
+
+void create()
+{
+ ::create();
+ Set(P_EARMUFFS, 0);
+ Set(P_EARMUFFS, SAVE, F_MODE);
+ Set(P_EARMUFFS, SECURED, F_MODE);
+ Set(P_INTERMUD, SAVE, F_MODE);
+ Set(P_IGNORE, ([]), F_VALUE);
+ Set(P_IGNORE, SAVE, F_MODE);
+ Set(P_BUFFER, SAVE, F_MODE);
+ Set(P_MESSAGE_PREPEND, SAVE, F_MODE_AS);
+ Set(P_MESSAGE_BEEP, SAVE, F_MODE_AS);
+}
+
+void create_super()
+{
+ set_next_reset(-1);
+}
+
+// uebermittelt eine MT_NOTIFICATION an this_object(), welche nicht ignoriert
+// werden kann und auch nicht gespeichert wird.
+protected void _notify(string msg, string action) {
+ ReceiveMsg(msg,
+ MT_NOTIFICATION|MSG_DONT_BUFFER|MSG_DONT_IGNORE|MSG_DONT_STORE,
+ action, 0, this_object());
+}
+
+protected void updates_after_restore(int newflag) {
+ // Altes Ignoriere loeschen...
+ mixed ign = Query(P_IGNORE,F_VALUE);
+ if (!mappingp(ign))
+ {
+ if (pointerp(ign))
+ _notify(break_string(
+ "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
+ "weil es eine Aktualisierung der Ignorierefunktion gab, "
+ "bei der eine Konversion der Daten leider nicht "
+ "moeglich war.",78), 0);
+
+ Set(P_IGNORE, ([]), F_VALUE);
+ }
+}
+
+static int set_report(string str) {
+ int canflags = QueryProp(P_CAN_FLAGS);
+
+ if(!str)
+ {
+ if (stat_reports) {
+ string *res=({});
+ if (stat_reports & DO_REPORT_HP)
+ res+=({"Lebenspunkte"});
+ if (stat_reports & DO_REPORT_SP)
+ res+=({"Konzentrationspunkte"});
+ if (stat_reports & DO_REPORT_POISON)
+ res+=({"Vergiftungen"});
+ if (stat_reports & DO_REPORT_WIMPY)
+ res+=({"Vorsicht"});
+
+ tell_object(ME,break_string(
+ "Dir werden jetzt Veraenderungen Deiner "
+ +CountUp(res) + " berichtet.",78));
+ }
+ else
+ tell_object(ME,
+ "Alle Statusreports sind ausgeschaltet.\n");
+
+ return 1;
+ }
+ else if (str == "aus") {
+ if (stat_reports & DO_REPORT_HP || stat_reports & DO_REPORT_WIMPY) {
+ string s="";
+ if (stat_reports & DO_REPORT_HP) {
+ str="ebenfalls ";
+ tell_object(ME, "Der Report wurde ausgeschaltet.\n");
+ }
+ if ( stat_reports & DO_REPORT_WIMPY ) {
+ tell_object(ME, "Der Vorsicht-Report wurde "+s+
+ "ausgeschaltet.\n");
+ }
+ stat_reports=0;
+ }
+ else {
+ tell_object(ME, "Der Report ist bereits ausgeschaltet.\n");
+ }
+ return 1;
+ }
+ else if (str == "ein") {
+ if ( stat_reports & DO_REPORT_HP ) {
+ tell_object(ME, "Der Report ist bereits eingeschaltet.\n");
+ return 1;
+ }
+ tell_object(ME, "Der Report wurde eingeschaltet.\n");
+ stat_reports |= DO_REPORT_HP;
+ if (!(canflags & CAN_REPORT_SP)) {
+ if (QueryQuest("Hilf den Gnarfen")==1) {
+ SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_SP);
+ stat_reports |= DO_REPORT_SP;
+ }
+ else {
+ tell_object(ME, break_string(
+ "Fuer den Statusreport Deiner Konzentration musst Du jedoch "
+ "zunaechst die Quest \"Hilf den Gnarfen\" bestehen.",78));
+ }
+ }
+ else {
+ stat_reports |= DO_REPORT_SP;
+ }
+ if (!(canflags & CAN_REPORT_POISON)) {
+ if (QueryQuest("Katzenjammer")==1) {
+ SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_POISON);
+ stat_reports |= DO_REPORT_POISON;
+ }
+ else {
+ tell_object(ME, break_string(
+ "Fuer den Statusreport Deiner Vergiftung musst Du jedoch "
+ "zunaechst die Quest \"Katzenjammer\" bestehen.",78));
+ }
+ }
+ else {
+ stat_reports |= DO_REPORT_POISON;
+ }
+ // Cache loeschen, damit beim naechsten Report-Event alle Daten neu
+ // eingetragen werden muessen. Muss beim Einschalten des Reports
+ // passieren, weil auch in der inaktiven Zeit weiterhin Aenderungen in
+ // status_report() eingehen, so dass der Cache zwar erst einmal leer ist,
+ // aber beim Wiedereinschalten nicht mehr ungueltig waere und somit
+ // veraltete Daten an den Spieler ausgegeben werden. Im unguenstigsten
+ // Fall wuerde das sogar dazu fuehren, dass die veralteten Daten lange
+ // Zeit nicht aktualisiert werden, wenn z.B. P_HP == P_MAX_HP, so dass
+ // kein P_HP-Event mehr eingeht.
+ report_cache=0;
+ }
+ else if (str == "vorsicht") {
+ if (!(canflags & CAN_REPORT_WIMPY)) {
+ if (QueryQuest("Schrat kann nicht einschlafen")==1) {
+ SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_WIMPY);
+ tell_object(ME, "Der Vorsicht-Report wurde eingeschaltet.\n");
+ stat_reports |= DO_REPORT_WIMPY;
+ }
+ else {
+ tell_object(ME, break_string(
+ "Fuer den Statusreport Deiner Vorsicht musst Du "
+ "zunaechst die Quest \"Schrat kann nicht einschlafen\" "
+ "bestehen.",78));
+ }
+ }
+ else
+ {
+ stat_reports |= DO_REPORT_WIMPY;
+ }
+ // fuer Seher auch Bericht der Fluchtrichtung einschalten.
+ if ((stat_reports & DO_REPORT_WIMPY)
+ && !(stat_reports & DO_REPORT_WIMPY_DIR)
+ && ((canflags & CAN_REPORT_WIMPY) || IS_SEER(ME)))
+ {
+ stat_reports |= DO_REPORT_WIMPY_DIR;
+ }
+ }
+ // sendet einmalig genau jetzt den konfigurierten report. Kann zum testen
+ // (von Triggern) oder beim Login benutzt werden, wenn man einen initialen
+ // Datenbestand erhalten will.
+ else if (str=="senden")
+ {
+ // Es wird Ausgabe von LP und Vorsicht getriggert, das sendet beide
+ // Zeilen.
+ status_report(DO_REPORT_HP, QueryProp(P_HP));
+ status_report(DO_REPORT_WIMPY, QueryProp(P_WIMPY));
+ return 1;
+ }
+ else
+ return 0;
+ // nur aktuellen Zustand berichten
+ set_report(0);
+ return 1;
+}
+
+private string get_poison_desc(int p) {
+ string ret;
+ if ( intp(p) ) {
+ switch(p) {
+ case 0: ret="keins"; break;
+ case 1..3: ret="leicht"; break;
+ case 4..8: ret="gefaehrlich"; break;
+ default: ret="sehr ernst"; break;
+ }
+ return ret;
+ }
+ else return "(nicht verfuegbar)";
+}
+
+// sprintf()-Formatstrings fuer die Reportausgabe.
+#define REPORTLINE "LP: %3d, KP: %3s, Gift: %s.\n"
+#define REPORTLINE_WIMPY "Vorsicht: %d, Fluchtrichtung: %s.\n"
+// Defines zur Adressierung der Cache-Eintraege
+#define REP_HP 0
+#define REP_SP 1
+#define REP_POISON 2
+
+protected void status_report(int type, mixed val) {
+ // Wenn der Spieler GMCP hat und das sich um die Information kuemmert,
+ // erfolgt keine textuelle Ausgabe mehr. Daher return, wenn GMCP_Char()
+ // erfolg vermeldet hat.
+ int flags = QueryProp(P_CAN_FLAGS);
+ switch (type) {
+ case DO_REPORT_HP:
+ if (GMCP_Char( ([ P_HP: val ]) ) ) return;
+ break;
+ case DO_REPORT_SP:
+ if (!(flags & CAN_REPORT_SP)) return;
+ if (GMCP_Char( ([ P_SP: val ]) ) ) return;
+ break;
+ case DO_REPORT_POISON:
+ if (!(flags & CAN_REPORT_POISON)) return;
+ if (GMCP_Char( ([ P_POISON: val ]) ) ) return;
+ break;
+ case DO_REPORT_WIMPY:
+ if (!(flags & CAN_REPORT_WIMPY)) return;
+ if (GMCP_Char( ([ P_WIMPY: val ]) ) ) return;
+ break;
+ case DO_REPORT_WIMPY_DIR:
+ if (!(flags & CAN_REPORT_WIMPY_DIR)) return;
+ if (GMCP_Char( ([ P_WIMPY_DIRECTION: val ]) ) ) return;
+ break;
+ }
+
+ // konventionelle textuelle Ausgabe des Reports ab hier.
+ if (!(type & stat_reports))
+ return;
+
+ if ( !report_cache ) {
+ report_cache = ({
+ QueryProp(P_HP),
+ (stat_reports&DO_REPORT_SP) ? to_string(QueryProp(P_SP)) : "###",
+ (stat_reports&DO_REPORT_POISON) ?
+ get_poison_desc(QueryProp(P_POISON)) : "(nicht verfuegbar)"
+ });
+ }
+
+ switch(type) {
+ // LP berichten: Cache aktualisieren und Meldung ausgeben.
+ case DO_REPORT_HP:
+ report_cache[REP_HP]=val;
+ tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
+ report_cache[REP_SP], report_cache[REP_POISON]));
+ break;
+ // KP berichten: Wenn der Spieler den Report freigeschaltet hat,
+ // wird bei Aenderungen gemeldet. Wenn nicht, aendert sich nur der
+ // Cache-Eintrag. So wird verhindert, dass ein Spieler ueber KP-
+ // Veraenderungen auch dann informiert wuerde, wenn er den KP-Report
+ // gar nicht benutzen koennte.
+ case DO_REPORT_SP:
+ report_cache[REP_SP]=to_string(val);
+ tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
+ report_cache[REP_SP], report_cache[REP_POISON]));
+ break;
+ // Giftstatus berichten: Wenn der Giftreport freigeschaltet ist,
+ // Cache aktualisieren und berichten. Wenn nicht, aendert sich nur
+ // der Cache-Eintrag. Erlaeuterung hierzu s.o. beim KP-Report.
+ case DO_REPORT_POISON:
+ report_cache[REP_POISON] = get_poison_desc(val);
+ tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
+ report_cache[REP_SP], report_cache[REP_POISON]));
+ break;
+ // Vorsicht-Report: kann ohne weitere Abfragen ausgegeben werden, da
+ // alle noetigen Checks schon zu Beginn dieser Funktion erledigt wurden.
+ // Lediglich der Inhalt der Meldung muss abhaengig vom Seherstatus
+ // konfiguriert werden.
+ case DO_REPORT_WIMPY:
+ string res;
+ if (IS_SEER(ME)) {
+ // QueryProp() aus Kostengruenden im if(), damit die Aufruf-
+ // Haeufigkeit zumindest ein wenig reduziert wird.
+ string dir = QueryProp(P_WIMPY_DIRECTION)||"keine";
+ res = sprintf(REPORTLINE_WIMPY, val, dir);
+ }
+ else
+ res = sprintf(REPORTLINE_WIMPY, val, "(nicht verfuegbar)");
+ tell_object(ME, res);
+ break;
+ // Fluchtrichtungs-Report: wird nur bei Sehern ausgegeben, damit
+ // nicht auch Spieler eine VS-/FR-Meldung bekommen, wenn z.B. eine
+ // externe Manipulation der Fluchtrichtung stattfindet, sie aber den
+ // Report mangels Seherstatus gar nicht freigeschaltet haben.
+ case DO_REPORT_WIMPY_DIR:
+ if (IS_SEER(ME)) {
+ if (!val) val = "keine";
+ tell_object(ME,sprintf(REPORTLINE_WIMPY, QueryProp(P_WIMPY), val));
+ }
+ break;
+ }
+}
+
+#undef REPORTLINE
+#undef REPORTLINE_WIMPY
+#undef REP_HP
+#undef REP_SP
+#undef REP_POISON
+
+private string permutate(string msg)
+{
+ // Kontrollzeichen rausfiltern. *seufz*
+ msg = regreplace(msg,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
+ object ob=QueryProp(P_PERM_STRING);
+ if (!objectp(ob))
+ return msg;
+
+ return (string)ob->permutate_string(msg)||"";
+}
+
+// neue nachricht an den Kobold anhaengen
+// Rueckgabewerte: MSG_BUFFER_FULL oder MSG_BUFFERED
+private int add_to_kobold(string msg, int msg_type, string msg_action,
+ string msg_prefix, object origin)
+{
+ // Nachricht soll im Kobold gespeichert werden.
+ // Kobold speichert Rohdaten und gibt spaeter das ganze auch wieder via
+ // ReceiveMsg() aus - dabei wird MSG_DONT_BUFFER | MSG_DONT_STORE gesetz,
+ // damit keine erneute Speicher in Kobold oder Komm-History erfolgt.
+
+ // wenn der Puffer zu klein ist, Groesse verdoppeln, wenn noch unterhalb
+ // des Limits.
+ if (kobold->index >= sizeof(kobold->buf)-1) {
+ if (sizeof(kobold->buf) < MAX_KOBOLD_LIMIT)
+ kobold->buf += allocate(sizeof(kobold->buf));
+ else
+ return MSG_BUFFER_FULL;
+ }
+ kobold->index = kobold->index +1;
+ // neue Nachricht an den Puffer anhaengen.
+ string sendername = query_once_interactive(origin) ?
+ origin->query_real_name() :
+ origin->name(WER) || "<Unbekannt>";
+ kobold->buf[kobold->index] = (<msg_s> msg: msg,
+ type : msg_type, action : msg_action, prefix : msg_prefix,
+ sendername : sendername);
+ return MSG_BUFFERED;
+}
+
+private void _flush_cache(int verbose) {
+ // nur mit genug Evalticks ausgeben.
+ if (get_eval_cost() < 100000) return;
+ if (kobold->index >= 0)
+ {
+ ReceiveMsg("Ein kleiner Kobold teilt Dir folgendes mit:",
+ MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
+ 0, 0, this_object());
+ int prepend = QueryProp(P_MESSAGE_PREPEND);
+ foreach(int i: 0 .. kobold->index) // '0 ..' ist wichtig!
+ {
+ struct msg_s msg = kobold->buf[i];
+ // dies ist dient der Fehlerabsicherung, falls es nen Fehler (z.B. TLE)
+ // in der Schleife unten gab: dann ist index nicht auf -1 gesetzt
+ // worden, aber einige Nachrichten sind schon geloescht.
+ if (!structp(msg)) continue;
+ // Ausgabe via efun::tell_object(), weil die Arbeit von ReceiveMsg()
+ // schon getan wurde. Allerdings muessen wir uns noch um den UMbruch
+ // kuemmern.
+ if ((msg->type) & MSG_DONT_WRAP)
+ msg->msg = (msg->prefix ? msg->prefix : "") + msg->msg;
+ else
+ {
+ int bsflags = msg->type & MSG_ALL_BS_FLAGS;
+ if (prepend)
+ bsflags |= BS_PREPEND_INDENT;
+ msg->msg = break_string(msg->msg, 78, msg->prefix, bsflags);
+ }
+ efun::tell_object(this_object(), msg->msg);
+ kobold->buf[i]=0;
+ }
+ kobold->index=-1;
+ }
+ else if (verbose)
+ {
+ ReceiveMsg("Der kleine Kobold hat leider nichts Neues fuer Dich.",
+ MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
+ 0, 0, this_object());
+ }
+}
+
+varargs int cmd_kobold(string arg)
+{
+ switch(arg)
+ {
+ case "ein":
+ SetProp(P_BUFFER, 1);
+ printf("Der Kobold merkt sich jetzt alles!\n"); break;
+ case "aus":
+ SetProp(P_BUFFER, 0);
+ printf("Der Kobold wird Dich nicht stoeren!\n"); break;
+ default: if(arg) printf("Der Kobold sagt: kobold ein oder kobold aus\n");
+ }
+ _flush_cache(1);
+ return 1;
+}
+
+public int TestIgnoreSimple(string *arg)
+{ string *ignore;
+
+ if (!pointerp(arg) || !mappingp(ignore=Query(P_IGNORE,F_VALUE)))
+ return 0;
+ foreach(string s: arg)
+ {
+ if (member(ignore,s))
+ return 1;
+ }
+ return 0;
+}
+
+//TODO: deprecated - entfernen, wenn Message() entfernt wird.
+private int check_ignore(mixed ignore, string verb, string name)
+{
+ if (ignore == verb)
+ return 1;
+ ignore = explode(ignore, ".");
+ return ((sizeof(ignore) > 1) &&
+ (name == ignore[0] && member(ignore[1..], verb) != -1));
+}
+
+private int comm_beep() {
+ if (QueryProp(P_VISUALBELL)) return 0; // kein ton
+ int beep_interval=(int)QueryProp(P_MESSAGE_BEEP);
+ if (!beep_interval || ((time()-last_beep_time) < beep_interval)) return 0;
+ last_beep_time=time();
+ return 1;
+}
+
+private varargs void add_to_tell_history( string uid, int sent, int recv,
+ string message, string indent, int flags )
+{
+ /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
+ Als Wert ist eine Strukur vom Typ chat_s eingetragen.
+ Strukturen chat_s und stored_msg_s sind in /std/player/comm_structs.c
+ definiert.
+ TODO fuer spaeter, gerade keine Zeit fuer:
+ Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
+ jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
+ weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
+ Element ist aeltestes Gespraech).
+ */
+
+ //TODO: Entfernen, wenn das nicht mehr passiert.
+ if (!stringp(uid))
+ {
+ ReceiveMsg(sprintf(
+ "\nadd_to_tell_history(): got bad uid argument %O."
+ "sent: %d, recv: %d, flags: %d, msg: %s",
+ uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);
+ }
+
+ // letzten Gespraechspartner fuer erwidere.
+ if (!(flags & MSGFLAG_REMOTE))
+ last_comm_partner = uid;
+
+ // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
+ // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
+ // Anfang.
+ if (sent) {
+ if (!sizeof(commreceivers))
+ commreceivers = ({uid});
+ else if (commreceivers[0] != uid) {
+ // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
+ // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
+ // entfernen.
+ // TODO: Effizienter gestalten.
+ commreceivers = ({uid}) + (commreceivers-({uid}));
+ // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
+ // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
+ // commreceivers drin sind.)
+ if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
+ commreceivers = commreceivers[0..MAX_SAVED_CHATS];
+ }
+ }
+
+ if (!tell_history_enabled)
+ return;
+
+ if (!indent && message[<1] == 10)
+ message = message[..<2];
+
+ struct chat_s chat;
+ // Gespraechspartner unbekannt?
+ if (!member(tell_history, uid)) {
+ // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
+ // dazu kommt.
+ if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
+ string deluid;
+ int zeit = __INT_MAX__;
+ foreach(string tuid, chat : tell_history) {
+ // aeltestes Gespraech suchen
+ if (zeit > chat->time_last_msg) {
+ deluid = tuid;
+ zeit = chat->time_last_msg;
+ }
+ }
+ // aeltestes Gespraech raus.
+ m_delete(tell_history, deluid);
+ if (member(commreceivers,deluid)>-1)
+ commreceivers-=({deluid});
+ }
+ // neues Gespraech anlegen
+ chat = (<chat_s> uid: uid, time_first_msg: time(),
+ time_last_msg: time(),
+ sentcount: sent, recvcount: recv,
+ msgbuf: 0, ptr: 0 );
+ tell_history[uid] = chat;
+ }
+ else {
+ // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
+ chat = tell_history[uid];
+ chat->time_last_msg = time();
+ chat->sentcount += sent;
+ chat->recvcount += recv;
+ }
+
+ if (tell_history_enabled < TELLHIST_ENABLED)
+ return;
+
+ // ggf. Array fuer Messages anlegen
+ if (!pointerp(chat->msgbuf))
+ chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
+
+ // Message-Struktur ermitteln oder neu anlegen
+ struct stored_msg_s msg;
+ if (!structp(chat->msgbuf[chat->ptr])) {
+ // neue Struct ins Array schreiben
+ chat->msgbuf[chat->ptr] = msg = (<stored_msg_s>);
+ }
+ else {
+ // alte Struct ueberschreiben
+ msg = chat->msgbuf[chat->ptr];
+ }
+ // Index auf naechste Messagestruktur ermitteln
+ chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
+ // Message speichern
+ msg->msg = message;
+ msg->prefix = indent;
+ msg->timestamp = time();
+}
+
+protected void clear_tell_history()
+{
+ /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
+ sofern der Spieler nichts abweichendes eingestellt hat. */
+
+#ifdef TELLHIST_LONGLIFE
+ if (tell_history_enabled == TELLHIST_LONGLIFE)
+ return;
+#endif
+
+ foreach (string uid, struct chat_s chat: tell_history)
+ if (pointerp(chat->msgbuf)) {
+ chat->msgbuf = 0;
+ chat->ptr = 0;
+ }
+}
+
+protected void reset(void)
+{
+ /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
+ Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
+ wie bei einem "schlafe ein" die Mitteilungen geloescht. */
+
+ if (!interactive())
+ clear_tell_history();
+
+}
+
+// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
+// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
+// werden soll.
+private void _send(object ob, string msg, int msg_type,
+ string msg_action, string msg_prefix)
+{
+ int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
+ switch(res) {
+ case MSG_DELIVERED:
+ break; // nix machen
+ case MSG_BUFFERED:
+ ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden."
+ "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
+ "genommen. Er wird sie spaeter weiterleiten!",
+ MT_NOTIFICATION, msg_action, 0, this_object());
+ break;
+ case MSG_IGNORED:
+ case MSG_VERB_IGN:
+ case MSG_MUD_IGN:
+ ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
+ MT_NOTIFICATION, msg_action, 0, this_object());
+ break;
+ case MSG_SENSE_BLOCK:
+ ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
+ MT_NOTIFICATION, msg_action, 0, this_object());
+ break;
+ case MSG_BUFFER_FULL:
+ ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden."
+ "Die Mitteilung ging verloren, denn der Kobold kann sich "
+ "nichts mehr merken!", MT_NOTIFICATION, msg_action,
+ 0, this_object());
+ break;
+ default:
+ ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
+ "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
+ break;
+ }
+}
+
+// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
+// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
+// die Nachricht ggf. in den Kommhistory erfasst wird.
+// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
+protected varargs int _recv(object ob, string message, int flag, string indent)
+{
+ write(break_string(message, 78, indent,
+ QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
+ if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
+ query_once_interactive(ob))
+ {
+ if (flag & MSGFLAG_WHISPER)
+ add_to_tell_history(getuid(ob), 1, 0,
+ "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
+ flag);
+ else
+ add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
+ }
+ return 1;
+}
+
+// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
+// Fehlerbehandlung) ist es jedoch auch mal ein String.
+varargs int Message(string msg, int flag, string indent,
+ string cname, mixed sender)
+{
+ object ti;
+ string verb, reply, *ignore, tin;
+ int em, te;
+ mixed deaf;
+
+ // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
+ // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
+ // damit 'ignoriere name.debug' funktioniert.
+ if( flag == MSGFLAG_CHANNEL ){
+ if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
+ || msg[1..9]=="Warnungen"))
+ {
+ // Missbrauch der Variable 'ignore' als Zwischenspeicher
+ ignore = regexplode( msg, ":| |\\]" );
+ verb = lower_case(ignore[0][1..]);
+ tin = lower_case(ignore[2]);
+ }
+ else
+ {
+ if(cname)
+ verb=lower_case(cname);
+ else
+ verb=query_verb();
+ if( ti = this_interactive() )
+ {
+ tin = getuid(this_interactive());
+ }
+ else
+ {
+ //falls doch kein Objekt...
+ if (objectp(sender))
+ tin=lower_case(sender->name(RAW)||"<Unbekannt>");
+ }
+ }
+ }
+ else {
+ if( ti = this_interactive() )
+ tin = getuid(this_interactive());
+ verb = query_verb();
+ }
+
+ te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
+
+ // fuer "erwidere"
+ if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
+ if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
+ if (flag & MSGFLAG_WHISPER)
+ add_to_tell_history(getuid(ti), 0, 1,
+ capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
+ (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
+ ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
+ " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
+ else
+ add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
+ }
+ }
+ // Hoert der Spieler nicht?
+ em = (ti &&
+ (te || flag & MSGFLAG_SHOUT) &&
+ (QueryProp(P_EARMUFFS) &&
+ (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
+ ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
+
+ // Werden der Sender oder das Verb ignoriert?
+ if(!ti && tin && flag == MSGFLAG_CHANNEL)
+ {
+ if((member(ignore, tin) != -1))
+ {
+ return MESSAGE_IGNORE_YOU;
+ }
+ if(verb && sizeof(filter(ignore, #'check_ignore, verb, tin)) )
+ {
+ return MESSAGE_IGNORE_YOU;
+ }
+ }
+ if (ti && (member(ignore, getuid(ti)) != -1)) {
+ if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
+ efun::tell_object(ti, capitalize(name())+
+ " hoert gar nicht zu, was Du sagst.\n");
+ return MESSAGE_IGNORE_YOU;
+ }
+ if(tin && verb &&
+ sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
+ {
+ if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
+ verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
+ efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
+ return MESSAGE_IGNORE_VERB;
+ }
+ if (flag & MSGFLAG_RTELL) {
+ int at;
+
+ verb = lower_case(old_explode(msg, " ")[0][1..]);
+ at = member(verb, '@');
+ /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
+ add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
+
+ if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
+ return MESSAGE_IGNORE_YOU;
+ else if (at > 0 && member(ignore, verb[at..]) >= 0)
+ return MESSAGE_IGNORE_MUD;
+ }
+
+ // Taubheit/Oropax
+ te |= (flag & MSGFLAG_SAY);
+
+ if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
+ deaf = QueryProp(P_DEAF);
+ if (te)
+ reply = stringp(deaf) ?
+ capitalize(sprintf(deaf, name())) :
+ capitalize(name())+" ist momentan leider taub.\n";
+ }
+ else if (em)
+ reply = capitalize(name())+" hat Oropax in den Ohren.\n";
+
+ msg = break_string(msg, 78, indent,
+ (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
+
+ if(QueryProp(P_BUFFER) &&
+ (deaf ||
+ query_editing(this_object()) ||
+ query_input_pending(this_object())))
+ {
+ deaf = MESSAGE_DEAF;
+ if(flag & MSGFLAG_CACHE)
+ {
+ if(!stringp(reply))
+ reply = name()+" moechte gerade nicht gestoert werden.\n";
+
+ msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
+
+ int res = add_to_kobold(msg, 0, 0, 0,
+ objectp(sender) ? sender : ME);
+ if(res == MSG_BUFFERED)
+ {
+
+ reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
+ "genommen.\nEr wird sie spaeter weiterleiten!";
+ deaf = MESSAGE_CACHE;
+ }
+ else {
+ reply += "Die Mitteilung ging verloren, denn "+
+ "der Kobold kann sich nichts mehr merken!";
+ deaf = MESSAGE_CACHE_FULL;
+ }
+ if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
+ efun::tell_object(ti, reply+"\n");
+ }
+ return deaf;
+ }
+ else if((deaf || em) &&
+ ( (flag & MSGFLAG_RTELL) ||
+ (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
+ if (te && ti)
+ efun::tell_object(ti, reply);
+ return MESSAGE_DEAF;
+ }
+
+ _flush_cache(0);
+ if(te && QueryProp(P_AWAY))
+ msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
+
+ if (flag & (MSGFLAG_SAY | MSGFLAG_TELL) && comm_beep()) {
+ msg=MESSAGE_BEEP+msg;
+ }
+ efun::tell_object(ME, msg);
+ return MESSAGE_OK;
+}
+
+static int ignoriere(string str)
+{
+ str = _unparsed_args(1);
+ mapping ignore=Query(P_IGNORE, F_VALUE);
+
+ if (!str)
+ {
+ string* ignarr = m_indices(ignore);
+ if (!sizeof(ignarr))
+ tell_object(ME, "Du ignorierst niemanden.\n");
+ else
+ ReceiveMsg("Du ignorierst:\n"
+ + break_string(CountUp(map(sort_array(ignarr, #'> ),
+ #'capitalize )
+ ) + ".",78),
+ MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
+ 0,0,this_object());
+ return 1;
+ }
+ // trim spaces from args and convert to lower case.
+ str = lower_case(trim(str, TRIM_BOTH));
+
+ if (member(ignore, str))
+ {
+ RemoveIgnore(str);
+ tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
+ }
+ else if (sizeof(ignore)>100)
+ {
+ tell_object(ME, "Du ignorierst schon genuegend!\n");
+ }
+ else if (AddIgnore(str) == 1)
+ {
+ tell_object(ME,
+ sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
+ }
+ else
+ {
+ tell_object(ME,
+ sprintf("'%s' kannst Du nicht ignorieren.\n",str));
+ }
+ return 1;
+}
+
+
+static int _msg_beep(string str) {
+ int beep_interval;
+ notify_fail("Syntax: klingelton <1 bis 3600 Sekunden> oder klingelton aus\n");
+ if (stringp(str)) {
+ if (str=="aus")
+ SetProp(P_MESSAGE_BEEP,0);
+ else if ((beep_interval=to_int(str)) > 0 && beep_interval<=3600)
+ SetProp(P_MESSAGE_BEEP,beep_interval);
+ else return 0;
+ }
+
+ beep_interval=(int)QueryProp(P_MESSAGE_BEEP);
+ _notify("Ton bei Mitteilungen: "+
+ (beep_interval ? "aller "+beep_interval+" Sekunden." : "aus."),
+ query_verb());
+ return 1;
+}
+
+static int _msg_prepend(string str) {
+ int beep_interval;
+ notify_fail("Syntax: senderwiederholung ein/aus\n");
+ if (stringp(str)) {
+ if (str=="aus")
+ SetProp(P_MESSAGE_PREPEND,1);
+ else if (str=="ein")
+ SetProp(P_MESSAGE_PREPEND,0);
+ else return 0;
+ }
+
+ _notify("Senderwiederholung bei Mitteilungen: "+
+ ((int)QueryProp(P_MESSAGE_PREPEND) ? "aus" : "ein")+".",
+ query_verb());
+
+ return 1;
+}
+
+static int _communicate(mixed str, int silent)
+{
+ string verb;
+ string myname;
+ string msg;
+
+ if (!str || extern_call()) str=_unparsed_args()||"";
+ /* str=_unparsed_args()||""; */
+ verb = query_verb();
+ if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
+ if (str==""||str==" "||!str)
+ {
+ _notify("Was willst Du sagen?",MA_SAY);
+ return 1;
+ }
+ msg=permutate(str);
+
+ myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
+ !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
+ "":QueryProp(P_PRESAY))+name())||"";
+
+ // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
+ // ist, keine MT_COMM)
+ send_room(environment(), msg, MT_LISTEN, MA_SAY,
+ capitalize(myname)+" sagt: ", ({this_object()}) );
+
+ if(!silent)
+ {
+ ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
+ MA_SAY, "Du sagst: ", ME);
+ }
+ return 1;
+}
+
+static int _shout_to_all(mixed str)
+{
+ string pre, myname, realname, wizards_msg, players_msg;
+ string wizard_prefix, player_prefix;
+ int chars;
+
+ if (!(str=_unparsed_args()))
+ {
+ _notify("Was willst Du rufen?",MA_SHOUT);
+ return 1;
+ }
+ chars=sizeof(str)/2;
+ if (chars<4) chars=4;
+ pre = (!IS_LEARNER(ME) ||
+ QueryProp(P_INVIS) ||
+ !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
+ realname = capitalize((pre + capitalize(getuid()))||"");
+ myname = capitalize(pre + name()||"");
+ if (QueryProp(P_INVIS))
+ realname = "("+realname+")";
+
+ wizards_msg = permutate(str);
+ wizard_prefix = myname+" ruft: ";
+
+ if(QueryProp(P_FROG)) {
+ players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
+ player_prefix = myname+" quakt: ";
+ }
+ else {
+ players_msg = wizards_msg;
+ player_prefix = wizard_prefix;
+ }
+
+ if(!IS_LEARNER(this_player()))
+ {
+ if(QueryProp(P_GHOST)) {
+ _notify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
+ MA_SHOUT);
+ return 1;
+ }
+ if (QueryProp(P_SP) <(chars+20))
+ {
+ _notify("Du musst erst wieder magische Kraefte sammeln.",
+ MA_SHOUT);
+ _notify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
+ "Ebenen').", MA_SHOUT);
+ return 1;
+ }
+ SetProp(P_SP, QueryProp(P_SP) - chars - 20);
+ }
+
+ ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
+ "rufe", "Du rufst: ", ME);
+
+ foreach ( object ob : users()-({this_object()}) )
+ if ( IS_LEARNER(ob) )
+ ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR, MA_SHOUT, wizard_prefix,
+ this_object());
+ else
+ ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR, MA_SHOUT, player_prefix,
+ this_object());
+
+ return 1;
+}
+
+varargs int _tell(string who, mixed msg)
+{
+ object ob;
+ string away,myname,ret;
+ mixed ignore,it;
+ string *xname;
+ int i,visflag;
+
+ if (extern_call() && this_interactive()!=ME) return 1;
+ if (!who || !msg) {
+ _notify("Was willst Du mitteilen?",MA_TELL);
+ return 1;
+ }
+
+ if(who == ERWIDER_PARAM)
+ {
+ if (!last_comm_partner)
+ {
+ _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
+ "Du was erwidern\nkoenntest.\n");
+ return 0;
+ }
+ who=last_comm_partner;
+ }
+
+ // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
+ if (who == ".")
+ who = ".1";
+
+ if ( sscanf(who, ".%d", i) == 1 ) {
+ if(i > 0 && i <= sizeof(commreceivers))
+ who = commreceivers[i-1];
+ else {
+ _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
+ return 0;
+ }
+ }
+
+ xname = explode(who, "@");
+
+ if (sizeof(xname) == 2)
+ {
+ if ( QueryProp(P_QP) )
+ {
+ if (ret=(string)INETD->_send_udp(xname[1],
+ ([ REQUEST: "tell",
+ RECIPIENT: xname[0],
+ SENDER: getuid(ME),
+ DATA: msg ]), 1))
+ {
+ _notify(ret, MA_TELL);
+ }
+ else
+ {
+ write("Nachricht abgeschickt.\n");
+ add_to_tell_history(who, 1, 0, msg,
+ "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_TELL, 1);
+ }
+ }
+ else
+ write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
+ "Muds etwas mitteilen zu koennen.\n");
+ return 1;
+ }
+
+ if (!ob=find_player(it = lower_case(who)))
+ {
+ it = match_living(it, 0);
+ if (!stringp(it))
+ switch(it) {
+ case -1:
+ _notify("Das war nicht eindeutig!",MA_TELL);
+ return 1;
+ case -2:
+ _notify("Kein solcher Spieler!",MA_TELL);
+ return 1;
+ }
+ ob = find_player(it) || find_living(it);
+ if(!ob) {
+ _notify("Kein solcher Spieler!",MA_TELL);
+ return 1;
+ }
+ }
+
+ if(QueryProp(P_INVIS)){
+ if(!IS_LEARNER(ob))
+ myname = name();
+ else
+ myname="("+
+ ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
+ capitalize(getuid()) + ")";
+ }
+ else
+ myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
+ QueryProp(P_PRESAY):"") + name();
+ if (myname && sizeof(myname)) myname=capitalize(myname);
+ // erstmal an Empfaenger senden
+ _send(ob, permutate(msg), MT_COMM|MT_FAR, MA_TELL,
+ myname + " teilt Dir mit: ");
+
+ // dann evtl. noch an Absender ausgeben...
+ if (visflag = !ob->QueryProp(P_INVIS) || IS_LEARNER(this_player()))
+ _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(it) + " mit: ");
+ // oder irgendwas anderes an den Absender ausgeben...
+ if (!visflag && interactive(ob))
+ _notify("Kein solcher Spieler!",MA_TELL);
+ else if (away = (string)ob->QueryProp(P_AWAY))
+ ReceiveMsg( break_string( away, 78, capitalize(it)
+ + " ist gerade nicht da: ", BS_INDENT_ONCE ),
+ MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
+ MA_TELL, 0, this_object());
+ else if (interactive(ob) && (i=query_idle(ob))>=600)
+ { //ab 10 Mins
+ if (i<3600)
+ away=time2string("%m %M",i);
+ else
+ away=time2string("%h %H und %m %M",i);
+
+ _notify(sprintf("%s ist seit %s voellig untaetig.",
+ capitalize(it),away),
+ MA_TELL);
+ }
+
+ return 1;
+}
+
+static int _teile(string str)
+{
+ string who, message;
+ if (!(str=_unparsed_args())) return 0;
+ if (sscanf(str, "%s mit %s", who, message) == 2)
+ return _tell(who, message,1);
+ return 0;
+}
+static int _teile_mit_alias(string str)
+{
+ str = _unparsed_args(), TRIM_LEFT;
+ if (!str) return 0;
+ str = trim(str, TRIM_LEFT);
+ // Ziel muss min. 2 Buchstaben haben (.<nr>)
+ if (sizeof(str) < 4) return 0;
+ int pos = strstr(str, " ");
+ if (pos >= 2)
+ return _tell(str[..pos-1], str[pos+1..]);
+ return 0;
+}
+
+static int _erzaehle(string str)
+{
+ string who, message;
+
+ if (!(str=_unparsed_args())) return 0;
+ if (sscanf(str, "%s %s", who, message) == 2)
+ return _tell(who, message,1);
+ return 0;
+}
+
+static int _whisper(string str)
+{
+ object ob;
+ string who;
+ string msg;
+ string myname;
+
+ if (!(str=_unparsed_args()) ||
+ (sscanf(str, "%s zu %s", who, msg) != 2 &&
+ sscanf(str, "%s %s", who, msg) !=2 )) {
+ _notify("Was willst Du wem zufluestern?",MA_SAY);
+ return 1;
+ }
+ if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
+ _notify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
+ return 1;
+ }
+
+ myname = capitalize((((IS_LEARNER(ME) &&
+ !QueryProp(P_INVIS) &&
+ (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
+ QueryProp(P_PRESAY) : "") + name()) || "");
+
+ _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
+ MSG_SAY, myname + " fluestert Dir zu: ");
+ send_room(environment(),
+ myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
+ MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
+
+ _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
+
+
+ return 1;
+}
+
+static int _remote_whisper(string str)
+{
+ /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
+ Nachricht nicht in der tell_history verewigt. */
+
+ object ob;
+ string who, it;
+ string msg;
+ string myname;
+
+ if (!(str=_unparsed_args()) ||
+ (sscanf(str, "%s zu %s", who, msg) != 2 &&
+ sscanf(str, "%s %s", who, msg) !=2 )) {
+ _notify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
+ return 1;
+ }
+
+ if (!ob=find_player(it = lower_case(who)))
+ {
+ it = match_living(it, 0);
+ if (!stringp(it))
+ switch(it){
+ case -1:
+ _notify("Das war nicht eindeutig!",MA_EMOTE);
+ return 1;
+ case -2:
+ _notify("Kein solcher Spieler!",MA_EMOTE);
+ return 1;
+ }
+ ob = find_player(it);
+ if(!ob) ob = find_living(it);
+ if(!ob){
+ _notify("Kein solcher Spieler!",MA_EMOTE);
+ return 1;
+ }
+ }
+ if (environment(ob) == environment()) {
+ _notify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
+ return 1;
+ }
+
+ myname = capitalize((((IS_LEARNER(ME) &&
+ !QueryProp(P_INVIS) &&
+ (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
+ QueryProp(P_PRESAY) : "") + name()) || "");
+
+ // An Empfaenger senden.
+ _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE, MA_EMOTE,
+ myname + " fluestert Dir aus der Ferne zu: ");
+
+ // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
+ if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
+ _notify("Kein solcher Spieler!",MA_EMOTE);
+ return 1;
+ }
+ // sonst eigene Meldung via _recv() ausgeben.
+ else
+ _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
+ "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
+
+ return 1;
+}
+
+static int _converse(string arg)
+{
+ _notify("Mit '**' wird das Gespraech beendet.",MA_SAY);
+ if (stringp(arg) && strstr(arg, "-s") == 0)
+ input_to("_converse_more", INPUT_PROMPT, "]", 1);
+ else
+ input_to("_converse_more", INPUT_PROMPT, "]", 0);
+ return 1;
+}
+
+static int _converse_more(mixed str, int silent)
+{
+ if (str == "**") {
+ _notify("Ok.",MA_SAY);
+ return 0;
+ }
+
+ if(str != "")
+ _communicate(str, silent);
+
+ input_to("_converse_more", INPUT_PROMPT, "]", silent);
+ return 1;
+}
+
+private int is_learner(object o) { return IS_LEARNER(o); }
+
+static int _shout_to_wizards(mixed str)
+{
+ int i, j;
+ string myname;
+ object *u;
+
+ str = _unparsed_args();
+ if (!str||!sizeof(str)) {
+ _notify("Was willst Du den Magiern zurufen?",MA_SHOUT);
+ return 1;
+ }
+ // Kontrollzeichen rausfiltern.
+ str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
+ myname = capitalize(getuid(this_object()));
+ if (!IS_LEARNER(this_object()))
+ _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
+
+ // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
+ filter(users(), #'is_learner)->ReceiveMsg(str,
+ MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE,
+ MA_SHOUT, myname+" an alle Magier: ", this_object());
+
+ return 1;
+}
+
+static int _echo(string str) {
+ if (!IS_SEER(ME) || (!IS_LEARNER(ME)
+ && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
+ return 0;
+
+ if (!(str=_unparsed_args())) {
+ _notify("Was moechtest Du 'echoen'?", 0);
+ return 1;
+ }
+
+ if (!IS_LEARNER(this_interactive()))
+ {
+ if (QueryProp(P_GHOST))
+ {
+ _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
+ return 0;
+ }
+ if (QueryProp(P_SP)<ECHO_COST)
+ {
+ _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
+ return 0;
+ }
+ SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
+ str=">\b"+str;
+ log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
+ str));
+ }
+ // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
+ // Kommhistory noch im Kobold landen.
+ send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
+ MA_UNKNOWN, 0, 0);
+ return 1;
+}
+
+// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
+// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
+// und gefedert.
+static string *_set_ignore(mixed arg)
+{
+ raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
+ "Benutze AddIgnore/RemoveIgnore()!\n");
+}
+// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
+// nix dran, dass alle TestIgnore() & Co benutzen sollen.
+static string *_query_ignore() {
+ mixed ign=Query(P_IGNORE, F_VALUE);
+ if (mappingp(ign))
+ return m_indices(ign);
+ return ({});
+}
+
+public int AddIgnore(string ign) {
+ // Einige strings sind nicht erlaubt, z.B. konsekutive .
+ if (!sizeof(ign)
+ || regmatch(ign,"[.]{2,}",RE_PCRE)
+ || regmatch(ign," ",RE_PCRE)
+ || sizeof(explode(ign,"."))>3)
+ return 0;
+
+ mapping ignores=Query(P_IGNORE, F_VALUE);
+ ignores[ign]=time();
+ // kein Set() noetig.
+ return 1;
+}
+
+public int RemoveIgnore(string ign)
+{
+ mapping ignores=Query(P_IGNORE,F_VALUE);
+ m_delete(ignores,ign);
+ // Kein Set() noetig
+ return 1;
+}
+
+static int _query_intermud()
+{
+ mixed tmp;
+ return member(pointerp(tmp=Query(P_CHANNELS))?tmp:({}), "Intermud") > -1;
+}
+
+
+int erwidere(string str)
+{
+ str=_unparsed_args();
+ if (!str) return 0;
+ return _tell(ERWIDER_PARAM, str ,1);
+}
+
+static int tmhist(string str)
+{
+
+ if (str == "aus") {
+ tell_history_enabled = TELLHIST_DISABLED;
+ write("Ok, es wird nichts mehr gespeichert.\n");
+ if (sizeof(tell_history)) {
+ tell_history = ([]);
+ commreceivers = ({});
+ write("Deine Mitteilungsgeschichte wurde geloescht.\n");
+ }
+ return 1;
+ }
+
+ if (str == "namen") {
+ int flag;
+ tell_history_enabled = TELLHIST_NO_MESSAGE;
+ write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
+ foreach (string uid, struct chat_s chat: tell_history)
+ if (pointerp(chat->msgbuf)) {
+ chat->msgbuf = 0;
+ chat->ptr = 0;
+ flag = 1;
+ }
+ if (flag)
+ write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
+ return 1;
+ }
+
+ if (str == "ein" || str == "an") {
+ tell_history_enabled = TELLHIST_ENABLED;
+ write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
+ return 1;
+ }
+
+#ifdef TELLHIST_LONGLIFE
+ if (str == "langlebig") {
+ tell_history_enabled = TELLHIST_LONGLIFE;
+ write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
+ "Ende/Crash/\nReboot gespeichert.\n");
+ return 1;
+ }
+#endif
+
+ if (str == "status") {
+ switch (tell_history_enabled) {
+ case TELLHIST_DISABLED:
+ write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
+ break;
+ case TELLHIST_NO_MESSAGE:
+ write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
+ break;
+ case TELLHIST_ENABLED:
+ write("Deine Mitteilungen werden gespeichert.\n");
+ break;
+#ifdef TELLHIST_LONGLIFE
+ case TELLHIST_LONGLIFE:
+ write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
+ "Crash/Reboot\ngespeichert.\n");
+ break;
+#endif
+ }
+ return 1;
+ }
+
+ if (tell_history_enabled == TELLHIST_DISABLED) {
+ _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
+ return 0;
+ }
+
+ if (!sizeof(tell_history)) {
+ _notify_fail("Du hast noch keinem etwas mitgeteilt "
+ "und noch keine Mitteilungen erhalten.\n");
+ return 0;
+ }
+
+ if (str && sizeof(str)) {
+
+ if (tell_history_enabled < TELLHIST_ENABLED) {
+ _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
+ return 0;
+ }
+
+ string uid;
+ if (member(tell_history, str)) {
+ // Name gewuenscht, da der String in der History vorkommt.
+ uid = str;
+ }
+ else {
+ // evtl. ne Zahl angegeben.
+ int i;
+ string *partners = sorted_commpartners(0);
+ if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
+ uid = partners[i];
+ else {
+ notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
+ return 0;
+ }
+ }
+
+ mixed *data = tell_history[uid]->msgbuf;
+ if (!data) {
+ _notify_fail(
+ "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
+ return 0;
+ }
+
+ int ptr = tell_history[uid]->ptr;
+
+ More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
+ data[0..ptr-1],
+ function string (struct stored_msg_s msg) {
+ if (!structp(msg)) return "";
+ return break_string( msg->msg + " <"
+ + strftime("%H:%M:%S",msg->timestamp) + ">", 78,
+ msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
+ } ) ) );
+ return 1;
+ }
+
+ string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
+ int i;
+ foreach (string uid : sorted_commpartners(0) ) {
+ int j;
+ struct chat_s chat = tell_history[uid];
+ history += sprintf("%2d.%-4s %s %-11s %d gesendet/%d empfangen\n", ++i,
+ ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
+ strftime("%a, %e.%m.%y",chat->time_last_msg),
+ capitalize(chat->uid), chat->sentcount, chat->recvcount);
+ }
+
+ More(history);
+
+ return 1;
+}
+
+static mixed _query_localcmds()
+{
+ return ({
+ ({"kobold", "cmd_kobold",0,0}),
+ ({"sag","_communicate",0,0}),
+ ({"sage","_communicate",0,0}),
+ ({"'","_communicate",1,0}),
+ ({"mruf","_shout_to_wizards",0,0}),
+ ({"mrufe","_shout_to_wizards",0,0}),
+ ({"ruf","_shout_to_all",0,0}),
+ ({"rufe","_shout_to_all",0,0}),
+ ({"erzaehl","_erzaehle",0,0}),
+ ({"erzaehle","_erzaehle",0,0}),
+ ({"teil","_teile",0,0}),
+ ({"teile","_teile",0,0}),
+ ({"tm","_teile_mit_alias",0,0}),
+ ({"fluester","_whisper",0,0}),
+ ({"fluestere","_whisper",0,0}),
+ ({"rfluester","_remote_whisper",0,0}),
+ ({"rfluestere","_remote_whisper",0,0}),
+ ({"gespraech","_converse",0,0}),
+ ({"echo","_echo",0,0}),
+ ({"ignorier","ignoriere",0,0}),
+ ({"ignoriere","ignoriere",0,0}),
+ ({"tmhist","tmhist",0,0}),
+ ({"erwider","erwidere",0,0}),
+ ({"erwidere","erwidere",0,0}),
+ ({"klingelton","_msg_beep",0,0}),
+ ({"senderwiederholung","_msg_prepend",0,0}),
+ ({"report","set_report",0,0}),
+ })+channel::_query_localcmds();
+}
+
+private string *sorted_commpartners(int reversed) {
+ return sort_array(m_indices(tell_history),
+ function int (string uid1, string uid2) {
+ if (reversed)
+ return tell_history[uid1]->time_last_msg >
+ tell_history[uid2]->time_last_msg;
+ else
+ return tell_history[uid1]->time_last_msg <=
+ tell_history[uid2]->time_last_msg;
+ } );
+}
+
+// Eigentlich nur in Magierobjekten gerufen. Gehoert aber thematisch hier
+// irgendwie hin.
+static void modify_prompt() {
+ string text = Query(P_PROMPT, F_VALUE);
+
+ if ( !stringp(text) || !sizeof(text) )
+ text = "> ";
+ else {
+ string path = Query(P_CURRENTDIR, F_VALUE);
+ if (stringp(path) && sizeof(path))
+ text = regreplace(text,"\\w",path,0); // Pfad einsetzen
+ }
+ configure_interactive(this_object(), IC_PROMPT, text);
+}
+
+// Prueft auf Ingoriereeintraege.
+// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
+#ifdef __LPC_UNIONS__
+public int TestIgnore(string|string* srcnames)
+#else
+public int TestIgnore(mixed srcnames)
+#endif
+{
+ mapping ign = Query(P_IGNORE, F_VALUE);
+ if (stringp(srcnames))
+ srcnames = ({srcnames});
+
+ foreach(string srcname: srcnames)
+ {
+ // einfachster Fall, exakter Match
+ if (member(ign, srcname))
+ return MSG_IGNORED;
+ // ansonsten muss aufgetrennt werden.
+ if (strstr(srcname,".") > -1)
+ {
+ string *srcparts=explode(srcname,".");
+ switch(sizeof(srcparts))
+ {
+ // case 0 und 1 kann nicht passieren.
+ case 3:
+ // zu pruefen: [sender].aktion.qualifizierer.
+ // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
+ // oben schon geprueft. im Spieler geprueft werden muss noch:
+ // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
+ if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
+ || member(ign, "."+srcparts[1]) // .aktion
+ || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
+ || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
+ )
+ {
+ return MSG_IGNORED;
+ }
+ break;
+ case 2:
+ // zu pruefen: spieler.aktion
+ // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
+ // oben schon geprueft. Im Spieler zu pruefen ist noch:
+ // spieler und .aktion
+ if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
+ || member(ign, "."+srcparts[1]))
+ {
+ return MSG_IGNORED;
+ }
+ break;
+ default: // mehr als 3 Teile...
+ raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
+ "is supported. Got: %s\n",srcname));
+ break;
+ }
+ }
+ }
+ // Default: nicht ignorieren.
+ return 0;
+}
+
+#ifdef __LPC_UNIONS__
+public int TestIgnoreExt(string|string* srcnames)
+#else
+public int TestIgnoreExt(mixed srcnames)
+#endif
+{
+ return TestIgnore(srcnames);
+}
+
+// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
+// Typen.
+// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
+private int check_ignores(string msg, int msg_type, string msg_action,
+ string msg_prefix, object origin)
+{
+ // Einige Dinge lassen sich nicht ignorieren.
+ if (msg_type & (MT_NEWS|MT_NOTIFICATION))
+ return 0;
+ // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
+ // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
+ // eine ignorierbare msg_action geben.
+ else if (stringp(msg_action) && origin && origin != ME)
+ {
+ string srcname =
+ (query_once_interactive(origin) ? origin->query_real_name()
+ : origin->name(WER) || "");
+ mapping ign = Query(P_IGNORE, F_VALUE);
+
+ if (member(ign, srcname))
+ return MSG_IGNORED;
+ // vielleicht wird irgendwas a la name.aktion ignoriert?
+ // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
+ // Ebenenmeldungen)
+ if (member(ign, srcname+"."+msg_action))
+ return MSG_VERB_IGN;
+ // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
+ // das reichlich sinnfrei ist.
+ if (member(ign, "."+msg_action))
+ return MSG_VERB_IGN;
+ // Spieler auf Ebenen ignoriert?
+ // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
+ if (strstr(msg_action, MA_CHANNEL) == 0)
+ {
+ // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
+ if (member(ign, srcname + "."MA_CHANNEL))
+ return MSG_IGNORED;
+ // spieler.ebenen.ebenenname ist oben schon abgedeckt.
+ // .ebenen halte ich fuer sinnfrei, nicht geprueft.
+ }
+ // Spieler aus anderem mud? *seufz*
+ if (strstr(srcname,"@") > -1)
+ {
+ string *srcparts = explode(srcname,"@");
+ if (sizeof(srcparts)==2)
+ {
+ // spieler@?
+ if (member(ign, srcparts[0]+"@"))
+ return MSG_IGNORED;
+ // oder Mud per @mud?
+ if (member(ign, "@" + srcparts[1]))
+ return MSG_MUD_IGN;
+ // BTW: spieler@mud wurde schon ganz oben erfasst.
+ }
+ }
+ }
+ // Default: nicht ignorieren.
+ return 0;
+}
+
+// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
+// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
+// BLindheit/Taubheit).
+// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
+// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
+// Sinn gar nix zu tun hat.
+// Rueckgabe: 0 oder MSG_SENSE_BLOCK
+private int check_senses(string msg, int msg_type, string msg_action,
+ string msg_prefix, object origin)
+{
+ int senses = msg_type & (MT_LOOK|MT_LISTEN);
+ // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
+ // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
+ // unterdrueckt werden.
+ if (!senses)
+ return 0;
+
+ if ((senses & MT_LOOK) && CannotSee(1))
+ senses &= ~MT_LOOK; // Sinn loeschen
+
+ if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
+ senses &= ~MT_LISTEN;
+
+ // wenn kein Sinn mehr ueber, wird die Nachricht nicht wahrgenommen.
+ if (!senses)
+ return MSG_SENSE_BLOCK;
+
+ return 0;
+}
+
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+ string msg_prefix, object origin)
+{
+ if (!msg) return MSG_FAILED;
+
+ // Flags und Typen spalten
+ int flags = msg_type & MSG_ALL_FLAGS;
+ int type = msg_type & ~flags;
+
+ // ggf. defaults ermitteln
+ origin ||= previous_object();
+ msg_action ||= comm_guess_action();
+ type ||= comm_guess_message_type(msg_action, origin);
+
+ // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
+ if (msg_type & MT_DEBUG)
+ {
+ if (!QueryProp(P_WIZ_DEBUG)
+ || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
+ return MSG_FAILED;
+ }
+
+ // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
+ // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
+ // Weiterbearbeitung.
+ // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
+ if (!(flags & MSG_DONT_IGNORE))
+ {
+ // Sinne pruefen. (nur typen uebergeben, keine Flags)
+ int res=check_senses(msg, type, msg_action, msg_prefix, origin);
+ if (res) return res;
+
+ // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
+ res=check_ignores(msg, type, msg_action, msg_prefix, origin);
+ if (res) return res;
+ }
+
+ // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
+ if ((type & MT_COMM))
+ {
+ // erstmal in der Komm-History ablegen, wenn gewuenscht.
+ if ((!(flags & MSG_DONT_STORE)))
+ {
+ string uid;
+ if (query_once_interactive(origin))
+ uid = origin->query_real_name();
+ else
+ uid = origin->name(WER) || "<unbekannt>";
+ add_to_tell_history(uid, 0, 1, msg, msg_prefix, 0);
+ }
+
+ // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
+ // Ebenenmeldungen. (Die haben ggf. schon.)
+ if (stringp(msg_action) && QueryProp(P_AWAY)
+ && strstr(msg_action, MA_CHANNEL) != 0)
+ {
+ // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
+ if (msg[<1] == '\n')
+ msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
+ else
+ msg = msg + " [" + strftime("%H:%M",time()) + "]";
+ }
+ // Kobold erlaubt und gewuenscht? Kobold ist fuer die
+ // direkte Kommunikation mittels MT_COMM vorgesehen.
+ // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
+ // ignorieren.
+ // die if-Konstruktion ist so, weil ich das _flush_cache() im else
+ // brauche.
+ if (query_editing(this_object()) || query_input_pending(this_object())
+ || QueryProp(P_EARMUFFS))
+ {
+ if (!(flags & MSG_DONT_BUFFER)
+ && QueryProp(P_BUFFER))
+ {
+ // Nachricht soll im Kobold gespeichert werden.
+ return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
+ }
+ }
+ else
+ {
+ // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
+ // entleeren.
+ _flush_cache(0);
+ }
+
+ // ggf. Piepston anhaengen. NACH Koboldablage, die sollen erstmal keinen
+ // Pieps kriegen.
+ if (comm_beep())
+ msg=msg + MESSAGE_BEEP;
+ }
+
+ // Ausgabenachricht bauen und an den Spieler senden.
+ if (flags & MSG_DONT_WRAP)
+ msg = (msg_prefix ? msg_prefix : "") + msg;
+ else
+ {
+ int bsflags = flags & MSG_ALL_BS_FLAGS;
+ if (QueryProp(P_MESSAGE_PREPEND))
+ bsflags |= BS_PREPEND_INDENT;
+ msg = break_string(msg, 78, msg_prefix, bsflags);
+ }
+ efun::tell_object(ME, msg);
+
+ return MSG_DELIVERED;
+}
diff --git a/std/player/comm_structs.c b/std/player/comm_structs.c
new file mode 100644
index 0000000..5f268eb
--- /dev/null
+++ b/std/player/comm_structs.c
@@ -0,0 +1,41 @@
+// MorgenGrauen MUDlib
+//
+// player/comm.c-- basic player communiction commands
+//
+// $Id: comm.c 6918 2008-08-07 21:13:16Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+struct msg_s {
+ string msg; // Inhalt der Nachricht
+ int type; // Messagetyp fuer ReceiveMsg
+ string action; // Messageaction fuer ReceiveMsg
+ string prefix; // Einrueckung der Nachricht bei Darstellung (msg_prefix)
+ string sendername;// Ursprung der Nachricht
+};
+
+struct stored_msg_s (msg_s) {
+ int timestamp; // Zeitstempel der Nachricht
+};
+
+struct msg_buffer_s {
+ //struct msg_s *buf;
+ mixed *buf;
+ int index;
+};
+
+struct chat_s {
+ string uid; // UID des Gespraechspartners
+ int time_first_msg; // Zeit der ersten Nachricht
+ int time_last_msg; // Zeit der letzen Nachricht
+ int sentcount; // Anzahl gesendeter Nachrichten
+ int recvcount; // Anzahl empfangener Nachrichten
+ mixed msgbuf; // Array von msg_s (Art Ringpuffer)
+ int ptr; // Pointer auf die naechste zu ueberschreibende msg_s
+ // in msgbuf
+};
+
diff --git a/std/player/command.c b/std/player/command.c
new file mode 100644
index 0000000..230445d
--- /dev/null
+++ b/std/player/command.c
@@ -0,0 +1,1074 @@
+// MorgenGrauen MUDlib
+//
+// player/commands.c -- alias, history and player command handling
+//
+// $Id: command.c 9576 2016-06-18 15:00:01Z Zesstra $
+#pragma strong_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player/command.h>
+#include <player/comm.h>
+#include <thing/properties.h>
+#include <living/moving.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <language.h>
+#include <new_skills.h>
+#include <config.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <logging.h>
+#include <strings.h>
+
+#define CBLOG(x) log_file(SHELLLOG("DISABLECOMMANDS"),x,200000)
+
+#define HIST_SIZE 40
+#define EPMASTER "/secure/explorationmaster"
+
+private mapping aliases;
+private string *commands;
+private int hist_size, show_processing, histmin;
+private string default_notify_fail;
+private nosave string *history, *unparsed_args, unmodified;
+private nosave int hist_now;
+private nosave object last_command_env;
+private nosave int cmds_per_time, last_chg, max_commands, *cmd_types;
+// Datenstruktur: ({Setzer, Ablaufzeit, String/Closure})
+private nosave mixed disablecommands;
+private nosave object* syntaxdb;
+
+nomask void __set_bb(int flag);
+
+static varargs int __auswerten(string str, string intern);
+varargs int SoulComm(string str, string _verb);
+varargs mixed More(string str, int fflag, string returnto);
+static int _starts_with(string str, string start);
+static void reallocate_histbuf();
+
+private void AddHistory(string str)
+{
+ if (!stringp(str) || str=="" || str[0]=='&' || str[0]=='^' ||
+ str=="hist")
+ return;
+ if (!hist_size) return;
+ if (!pointerp(history) || sizeof(history)!=hist_size)
+ reallocate_histbuf();
+ if (sizeof(str)>=histmin && history[(hist_size+hist_now-1)%hist_size]!=str)
+ history[(hist_now++)%hist_size]=str;
+}
+
+static void create()
+{
+ last_chg=0;
+ histmin=hist_now=0;
+ Set(P_LOCALCMDS,({}));
+ Set(P_LOCALCMDS,PROTECTED,F_MODE_AS);
+ Set("_syntaxdb", SECURED|SAVE, F_MODE_AS);
+
+ show_processing=1;
+ unparsed_args=({0,0,0});
+ hist_size=HIST_SIZE;
+}
+
+static int replacedisplay(string str)
+{
+ if (!str || str=="" || !sscanf(str,"%d",show_processing))
+ printf("Unzulaessige Eingabe!\n%s 0|1|2\n",query_verb());
+ printf("Ersetzungsanzeige auf Level %d.\nLevel 0: Nichts anzeigen\n"+
+ "Level 1: Nur History-Ersetzungen anzeigen\n"+
+ "Level 2: History- und Alias-Ersetzungen anzeigen\n",show_processing);
+ if (show_processing>2&&!IS_WIZARD(ME)) show_processing=2;
+ return 1;
+}
+
+static int histmin(string str)
+{
+ int len;
+
+ if (!str||!sscanf(str,"%d",len)||len<0)
+ {
+ write("Benutzung: histmin ZAHL\nLegt die Mindestlaenge fest, die eine \
+Befehlszeile haben muss, um in den\nHistory-Puffer zu gelangen. Derzeit \
+eingestellt auf "+(string)histmin+" Zeichen.\n");
+ return 1;
+ }
+ histmin=len;
+ write("Mindestlaenge auf "+(string)len+" eingestellt.\n");
+ return 1;
+}
+
+static void reallocate_histbuf()
+{
+ int i;
+
+ history=allocate(hist_size);
+ hist_now=0;
+ for (i=0;i<hist_size;i++)
+ if (!stringp(history[i]))
+ history[i]="\n\n";
+}
+
+static int histlen(string str)
+{
+ int d;
+ if (!str||!sscanf(str,"%d",d)||d<0||d>40)
+ {
+ write("Benutzung: histlen ZAHL\nZAHL muss zwischen 0 und 40 liegen.\n");
+ printf("Deine History-Buffer-Laenge liegt bei %d Befehlen.\n",hist_size);
+ return 1;
+ }
+ hist_size=d;
+ printf("Deine History-Buffer-Laenge liegt jetzt bei %d Befehlen.\n",
+ hist_size);
+ reallocate_histbuf();
+ return 1;
+}
+
+static void initialize()
+{
+ if (!pointerp(history)||sizeof(history)!=hist_size)
+ reallocate_histbuf();
+ add_action("__auswerten","",1);
+ max_commands = EPMASTER->QueryCommands();
+ cmd_types = EPMASTER->QueryCmdTypes() || ({});
+
+ if ( !mappingp(aliases) )
+ aliases = ([]);
+
+ if ( !pointerp(commands) )
+ commands = ({});
+
+ if (QueryProp("_syntaxdb"))
+ syntaxdb = ({find_object("/secure/syntaxdb")});
+/* else if (QueryProp(P_TESTPLAYER))
+ {
+ SetProp("_syntaxdb", 1);
+ call_out(#'_notify, 2,
+ "\nDa Du als Testspieler markiert bist, wurde bei Dir "
+ "die Syntaxsammlung eingeschaltet. Du kannst dies "
+ "wieder ausschalten. (hilfe syntaxsammlung) "
+ "Es waere schoen, wenn Du es beim Testen von "
+ "Gebieten einschaltest.", 0);
+ }*/
+}
+
+static mixed _set_default_notify_fail(string s)
+{
+ if (stringp(s)&&s!="")
+ {
+ if (s[<1]!='\n') s+="\n";
+ return default_notify_fail=s;
+ }
+ else if (!s||s=="")
+ return (default_notify_fail=0);
+}
+
+static mixed _query_default_notify_fail()
+{
+ return default_notify_fail;
+}
+
+static int set_errormessage(string s)
+{
+ if (!(s=_unparsed_args()))
+ {
+ _set_default_notify_fail(0);
+ write("Standard-Fehlermeldung auf \"Wie bitte?\" gesetzt.\n");
+ } else
+ {
+ write(break_string(sprintf("Standard-Fehlermeldung auf %s gesetzt.\n",
+ s),78));
+ _set_default_notify_fail(s);
+ }
+ return 1;
+}
+
+void reconnect()
+{
+ if (!mappingp(aliases)) aliases=([]);
+
+ if ( !pointerp(commands) )
+ commands = ({});
+
+ max_commands = EPMASTER->QueryCommands();
+ cmd_types = EPMASTER->QueryCmdTypes() || ({});
+}
+
+static int show_hist()
+{
+ int i;
+ string comm;
+
+ tell_object( ME, "Die History-Liste enthaelt folgende Kommandos:\n" );
+
+ for( i = 0; i < hist_size; i++ )
+ if ((comm=history[(hist_now+i)% hist_size])!= "\n\n")
+ tell_object( ME, " &"+(hist_now+i-hist_size)+"/-"+ (hist_size-i-1)
+ +"\t= "+comm+"\n");
+ return 1;
+}
+
+static string present_alias(mixed *ali)
+{
+ int j,k;
+ string s,s2;
+
+ for (s="",j=sizeof(ali)-1;j>=0;j--)
+ if (intp(ali[j]))
+ if ((k=ali[j])<0)
+ s="$"+(k==-1?"":(string)-k)+"*"+s;
+ else
+ s="$"+(string)k+s;
+ else
+ {
+ s2=implode(explode(ali[j],"\\"),"\\\\");
+ s=implode(explode(s2,"$"),"\\$")+s;
+ }
+ return s;
+}
+
+#define ALIFORMAT ({" %s\t= %s", "alias %s %s"})[display_as_aliascommand]
+// Ich weiss, den Variablennamen im define zu haben ist unfein, aber das
+// macht es im Code dann angenehm uebersichtlich. -HrT
+
+static int query_aliases(int display_as_aliascommand)
+{
+ int i;
+ string *a,*ali;
+
+ if(i=sizeof(ali=sort_array(m_indices(aliases),#'<))) //')))
+ {
+ for(a=({}),i--; i>=0; i--)
+ a+=({sprintf(ALIFORMAT, ali[i], present_alias( aliases[ali[i]] ) ) });
+ More("Du hast folgende Aliase definiert:\n"+implode(a,"\n"));
+ }
+ else
+ write("Du hast keine Aliase definiert.\n");
+ return 1;
+}
+
+static int
+_starts_with(string str, string start)
+{
+ return (sizeof(start)>sizeof(str) ? 0
+ : str[0..sizeof(start)-1]==start);
+}
+
+static int alias(string str)
+{
+ string command;
+ string *tmp,um,*hits;
+ int num, l, pos, cont;
+ int display_as_aliascommand;
+
+ if (unmodified&&unmodified!="")
+ um=implode(old_explode(unmodified," ")[1..]," ");
+ if (um=="") um=0;
+ if(!(str=um||_unparsed_args()) || str=="*") return query_aliases(0);
+
+ if (str=="-a" || strstr(str, "-a ")==0 ) {
+ str=str[2..];
+ if (str && str!="" && str[0]==' ') str=str[1..];
+ if (!str || str=="" || str=="*") return query_aliases(1);
+ display_as_aliascommand=1;
+ }
+
+ if ((pos=member(str,' '))<0) // 1 Arg only
+ {
+ if ((tmp=aliases[str]))
+ printf(ALIFORMAT+"\n",str,present_alias(tmp));
+ else
+ if (str[<1]=='*')
+ {
+ str=str[0..<2];
+ hits=filter(m_indices(aliases), #'_starts_with, str);
+ if (!sizeof(hits))
+ {
+ printf("Du hast kein Alias, das mit \"%s\" anfaengt.\n", str);
+ return 1;
+ }
+ hits=sort_array(hits, #'>);
+ for (l=sizeof(hits); l--;)
+ hits[l]=sprintf(ALIFORMAT, hits[l], present_alias(aliases[hits[l]]));
+ More("Folgende Aliase beginnen mit \""+str+"\":\n"+implode(hits,"\n"));
+ }
+ else
+ printf("Du hast kein Alias \"%s\" definiert.\n",str);
+ return 1;
+ }
+ if (!pos)
+ {
+ write("Fehler: Blanc am Alias-Anfang\n");
+ return 1;
+ }
+ if ((command=str[0..pos-1])=="unalias")
+ {
+ write
+ ("Es nicht moeglich, den Befehl unalias zu ueberladen (waer dumm :))\n");
+ return 1;
+ }
+ if ((command=str[0..pos-1])=="*")
+ {
+ write
+ ("Es nicht moeglich, den Befehl \"*\" zu ueberladen.\n");
+ return 1;
+ }
+
+ str=str[pos+1..],tmp=({});
+ while (l=sizeof(str)) {
+ pos=0,cont=1;
+ while (cont) {
+ if (pos<l) {
+ if(str[pos]=='\\') {
+ str=str[0..pos-1]+str[pos+1..];
+ l--;
+ } else {
+ if (str[pos]=='&' || str[pos]=='$') {
+ cont=0;
+ if (pos>0) {
+ tmp+=({str[0..pos-1]});
+ }
+ if (pos==l-1) {
+ printf("Fehler: %c am Zeilenende\n",str[pos]);
+ return 1;
+ }
+ if ((num=str[++pos])=='*') {
+ num=1;
+ pos--;
+ } else {
+ num-='0';
+ }
+ if (num<0 || num>9) {
+ printf("Fehler: Nach %c muss Ziffer oder * folgen\n",
+ str[pos-1]);
+ return 1;
+ }
+ if ((str=str[pos+1..])!=""&&str[0]=='*') {
+ str=str[1..];
+ num=-num;
+ }
+ tmp+=({num});
+ }
+ }
+ pos++;
+ } else {
+ cont=0;
+ if (str!="") tmp+=({str});
+ str="";
+ }
+ }
+ }
+ if ((!aliases[command]) && (sizeof(aliases)>2000))
+ printf("Du hast schon genuegend Aliase definiert!\n");
+ else
+ {
+ aliases[command]=tmp;
+ printf("Neues Alias: %s\t= %s\n",command,present_alias(tmp));
+ }
+ return 1;
+}
+
+static int unalias(string str) {
+ int i;
+ string *als,um;
+
+ if (unmodified&&unmodified!="")
+ um=implode(old_explode(unmodified," ")[1..]," ");
+ if (um=="") um=0;
+ if ( !(str=um || _unparsed_args())) return 0;
+ if (str == "*.*" || str == "*") {
+ write(break_string(
+ "Versuchs mal mit 'unalias .*', wenn Du wirklich alle Alias entfernen "
+ "willst.",78));
+ return 1;
+ }
+ if (!member(aliases,str)) {
+ als=regexp(m_indices(aliases),("^"+str+"$"));
+ if (!(i=sizeof(als))) {
+ write("So ein Alias hast Du nicht definiert.\n");
+ return 1;
+ }
+ for (--i;i>=0;i--)
+ m_delete(aliases,als[i]);
+ write(break_string(("Du entfernst folgende Aliase: "+
+ implode(als," ")+".\n"),75));
+ return 1;
+ }
+ m_delete(aliases,str);
+ write("Du entfernst das Alias \""+str+"\".\n");
+ return 1;
+}
+
+varargs string _unparsed_args(int level)
+{
+ return unparsed_args[level];
+}
+
+#define ARTIKEL ({"das","der","die","des","dem","den","ein","eine","einer",\
+ "eines"})
+
+#define TRENNER ({"in","aus","ueber","auf","unter","mit","durch","fuer",\
+ "von","vom","im","aufs","ein","weg","zurueck"})
+
+static string _single_spaces(string str)
+{
+ return regreplace(str, " *", " ", 1);
+}
+
+static mixed _return_args(string str)
+{
+ string *t,*t2,verb,s2;
+ int i,l,j,l2;
+
+ t=explode(trim(str,TRIM_BOTH)," ");
+ verb=t[0];
+ t = t[1..];
+ if (!sizeof(t))
+ {
+ unparsed_args[0]=unparsed_args[1]=unparsed_args[2]=0;
+ return str=verb;
+ }
+ else
+ str = unparsed_args[0] = implode(t, " ");
+
+ str=unparsed_args[1]=lower_case(_single_spaces(str));
+ t=regexplode(str,"\\<im\\>|\\<ins\\>");
+ for (i=1;i<sizeof(t);i+=2) t[i]="in";
+ t=regexplode(implode(t,""),"[\\,\\!\\:][\\,\\!\\:]*");
+ l=sizeof(t);
+ for(i=1;i<l;i+=2) t[i]="";
+ t=old_explode(implode(t,"")," ")-({""});
+ for (i=sizeof(t)-2;i>=0;i--)
+ {
+ if (member(ARTIKEL,t[i])>=0)
+ t=t[0..i-1]+t[i+1..];
+ }
+ unparsed_args[2]=implode(t," ");
+ t=regexplode((str=implode(t," ")),"[0-9][0-9]*\\.");
+ if ((l=sizeof(t))>2)
+ {
+ i=1;
+ while (i<l-1)
+ {
+ t[i]=" "+t[i][0..<2]+" ";
+ if ((l2=sizeof(t2=old_explode(t[i+1]," ")))<2)
+ t[i+1]+=t[i];
+ else
+ {
+ for (j=1;j<l2;j++)
+ {
+ if (member(TRENNER,t2[j])>=0)
+ {
+ t2[j-1]+=t[i];
+ l2=0;
+ }
+ }
+ if (!l2)
+ t[i+1]=implode(t2," ");
+ else
+ t[i+1]+=t[i];
+ }
+ t[i]="";
+ i+=2;
+ }
+ str=_single_spaces(verb+" "+implode(t," "));
+ if (str[<1]==' ') str=str[0..<2];
+ } else str=verb+(str==""?"":" "+str);
+ if (show_processing>2)
+ printf("-> {%s}\n",str);
+ return str;
+}
+
+static void decay_average()
+{
+ if (absolute_hb_count()-last_chg>14)
+ {
+ last_chg=absolute_hb_count()-last_chg;
+ if (last_chg>3000)
+ last_chg=absolute_hb_count(),cmds_per_time=0;
+ else
+ {
+ while (last_chg>14)
+ cmds_per_time=cmds_per_time*9/10, last_chg-=15;
+ last_chg=absolute_hb_count()-last_chg;
+ }
+ }
+}
+
+private void DelayPreparedSpells() {
+ mixed ps;
+
+ if (pointerp(ps=QueryProp(P_PREPARED_SPELL))
+ && sizeof(ps)>=1 && intp(ps[0])) {
+ ps[0]++;
+ SetProp(P_PREPARED_SPELL,ps);
+ write("Die Ausfuehrung Deines vorbereiteten Spruches wird verzoegert.\n");
+ } else if (ps) {
+ SetProp(P_PREPARED_SPELL,0);
+ }
+}
+
+static mixed bb;
+#ifndef BBMASTER
+#define BBMASTER "/secure/bbmaster"
+#endif
+
+/** Interpretiert Aliase und History-Kommandos
+ Eigentlich muesste hier noch die Umwandlung der Sonderzeichen
+ verschiedener Zeichensaetze mit convert_charset gemacht werden,
+ aber noch gibt es keine Moeglichkeit, den vom Spieler genutzten
+ Zeichensatz zu identifizieren.
+ \param[in] str string - Kommando des Spielers
+ \return interpretiertes Alias bzw. korrektes Kommando aus der History
+*/
+private string parsecommand(string str)
+{
+ if (str[0]=='\\')
+ {
+ // Kommando soll nicht interpretiert werden
+ return str[1..];
+ }
+ else if (str[0]=='&')
+ {
+ // Kommando aus der History
+ string cmd = str[1..];
+ int cmd_size = sizeof(cmd);
+ int cmd_found = 0;
+ if (cmd_size)
+ {
+ // Test ob &<text> etwas findet
+ for (int i=0;i<hist_size-1 && !cmd_found;i++)
+ {
+ int idx = (hist_size-i+hist_now-1)%hist_size;
+ if (history[idx][0..cmd_size-1]==cmd)
+ {
+ str = history[idx];
+ cmd_found = 1;
+ }
+ if (cmd_found)
+ {
+ if (show_processing)
+ printf("[%s]\n",str);
+ }
+ }
+ }
+ if (!cmd_found)
+ {
+ // Test, ob &<nr> klappt
+ int nummer;
+ if (str=="&&")
+ str = "&-0";
+ if (sscanf(str,"&%d",nummer))
+ {
+ if (nummer<0 || (!nummer && str[1]=='-'))
+ {
+ if (nummer<-(hist_size-1))
+ nummer=-1;
+ else
+ nummer=(hist_now+nummer-1+hist_size)%hist_size;
+ }
+ else
+ {
+ if (nummer>hist_now || hist_now-nummer>hist_size)
+ nummer=-1;
+ else
+ nummer=nummer%hist_size;
+ }
+ if (nummer<0
+ || ( (cmd=history[nummer]) =="\n\n") )
+ notify_fail("Der Befehl ist nicht in der History!\n");
+ else
+ {
+ str = cmd;
+ if (show_processing)
+ printf("[%s]\n",str);
+ }
+ }
+ }
+ }
+ switch (str)
+ {
+ case "n": return "norden";
+ case "s": return "sueden";
+ case "w": return "westen";
+ case "o": return "osten";
+ case "nw": return "nordwesten";
+ case "sw": return "suedwesten";
+ case "so": return "suedosten";
+ case "no": return "nordosten";
+ case "ob": return "oben";
+ case "u": return "unten";
+ }
+ // Test auf Alias
+ string output = "";
+ string* input = explode(str," ");
+ int input_size = sizeof(input);
+ mixed alias = aliases[input[0]];
+ if (!alias)
+ return str;
+ foreach (mixed a:alias)
+ {
+ if (!intp(a))
+ output += a;
+ else
+ {
+ if (a >= 0)
+ {
+ if (input_size > a)
+ output += input[a];
+ }
+ else
+ {
+ a = -a;
+ if (input_size > a)
+ output += implode(input[a..]," ");
+ }
+ }
+ }
+ output = _single_spaces(output);
+ str = trim(output,TRIM_RIGHT);
+ if (show_processing>1)
+ printf("[%s]\n",str);
+ return str;
+}
+
+/** Behandelt alle Sonderfaelle der Eingabe des Spielers
+ Alle Befehle des Spielers, die nicht durch Objekte behandelt
+ werden sollen, werden hier erkannt und ausgefuehrt.
+ Dazu gehoert auch die Interpretation von Aliases und History-
+ befehlen.
+ \param[in] str string: Kommando des Spielers
+ \return auszufuehrendes Kommando
+ oder 0 fuer ein nicht interpretierbares Kommando
+ oder 1 fuer ein bereits durchgefuehrtes Kommando
+*/
+mixed modify_command(string str)
+{
+
+ if (extern_call() && previous_object() &&
+ (previous_object()!=this_object() || process_call()) )
+ {
+ return 0;
+ }
+
+ // Leerzeichen an den Enden abschneiden.
+ str = trim(str, TRIM_BOTH);
+
+ if (bb)
+ BBMASTER->BBWrite(trim(str,TRIM_RIGHT,"\n"), 0);
+
+ decay_average();
+ cmds_per_time+=10000;
+
+ unparsed_args[0]=unparsed_args[1]=unparsed_args[2]=unmodified="";
+
+ if (!sizeof(str)) return "";
+
+ // Kommando wird geparst
+ unmodified=parsecommand(str);
+
+ // Environment schonmal merken.
+ last_command_env=environment();
+
+ if (unmodified == "")
+ return "";
+ // Kommando in History merken, auch wenn es im Kommandoblock abgebrochen
+ // wird.
+ AddHistory(unmodified);
+
+ // pruefen, ob Kommandoblock gesetzt ist.
+ // (Fuer Magier mit mschau ein wird das ignoriert.)
+ // BTW: Es wird absichtlich nicht das Ergebnis der Closure zurueckgegeben,
+ // sonst wuerde man beliebigen Objekten nicht nur das Abbrechen, sondern
+ // auch das Aendern von Kommandos ermoeglichen.
+ if (disablecommands && !IS_LEARNING(ME) )
+ {
+ if (disablecommands[B_TIME] >= time()
+ && objectp(disablecommands[B_OBJECT]))
+ {
+ // disablecommands valid
+ // hart-kodierte Ausnameliste pruefen
+ if ( member(({"mrufe","mschau","bug","idee","typo","detail"}),
+ explode(str," ")[0]) == -1)
+ {
+ if (closurep(disablecommands[B_VALUE]))
+ {
+ if (funcall(disablecommands[B_VALUE],_return_args(unmodified)))
+ {
+ // Non-zero Closure-Ergebnis, Abbruch. Die gerufene Funktion ist
+ // fuer eine geeignete Meldung an den Spieler verantwortlich.
+ return 1;
+ }
+ }
+ // wenn Text, dann auch pruefen, ob das Kommandoverb in den Ausnahmen
+ // steht. (query_verb() geht leider hier noch nicht.)
+ else if (stringp(disablecommands[B_VALUE])
+ && member(disablecommands[B_EXCEPTIONS],
+ explode(str," ")[0]) == -1)
+ {
+ // meldung ausgeben...
+ tell_object(PL, disablecommands[B_VALUE]);
+ // und Ende...
+ return 1;
+ }
+ }
+ }
+ else disablecommands=0;
+ }
+
+ // Verfolger direkt ins Env reinholen.
+ if (remove_call_out("TakeFollowers")>=0)
+ catch(TakeFollowers();publish);
+
+ DelayPreparedSpells();
+
+ // Historyeintrag korrigieren
+ if (unmodified[0]=='^')
+ {
+ string *oldnew,pre,post;
+ if (sizeof(oldnew=explode(unmodified,"^"))>2)
+ {
+ int hist_idx = (hist_now-1)%hist_size;
+ sscanf(history[hist_idx],"%s"+oldnew[1]+"%s", pre, post);
+ unmodified = pre+oldnew[2]+post;
+ if (show_processing)
+ write("["+unmodified+"]\n");
+ // korrigiertes Kommando natuerlich auch in die History.
+ AddHistory(unmodified);
+ }
+ }
+
+ if( bb )
+ BBMASTER->BBWrite(" -> " + unmodified, 1);
+
+ if (show_processing>1)
+ printf("[%s]\n",unmodified);
+
+ mixed ret = _return_args(unmodified);
+
+ // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
+ // dorthin melden.
+ if (syntaxdb)
+ {
+ if (!objectp(syntaxdb[0]))
+ syntaxdb[0] = find_object("/secure/syntaxdb");
+ if (syntaxdb[0])
+ catch(syntaxdb[0]->start_cmd(unmodified);nolog);
+ }
+ return ret;
+}
+
+static int do_list(string str)
+{
+ string *cmdlist;
+ int i;
+
+ if (!QueryProp(P_WANTS_TO_LEARN))
+ return 0;
+ cmdlist=old_explode(_unparsed_args()||"",";")-({ "" });
+ for (i=0;i<sizeof(cmdlist);i++)
+ {
+ cmdlist[i]=implode(old_explode(cmdlist[i]," ")-({}), " ");
+ if (show_processing)
+ write("["+cmdlist[i]+"]\n");
+ command(cmdlist[i]);
+ }
+ return 1;
+}
+
+//falls die aliasliste kaputt ist ...
+
+int unalias_all()
+{
+ if (IS_ELDER(this_interactive())) aliases=([]);
+ return 1;
+}
+
+object _query_last_command_env()
+{
+ return last_command_env;
+}
+
+int _query_show_alias_processing()
+{
+ return show_processing;
+}
+
+int _query_histmin()
+{
+ return histmin;
+}
+
+varargs void AddAction(mixed fun, mixed cmd, int flag, int lvl)
+{
+ int i;
+ mixed *cmds;
+
+ log_file( "ARCH/ADD_ACTION", sprintf(
+ "%s:\n TO: %O TP: %O PO: %O\n fun: %O cmd: %O flag: %O lvl: %O",
+ dtime(time()), this_object(), this_player(), previous_object(),
+ fun, cmd, flag, lvl));
+
+ if (!(cmds=Query(P_LOCALCMDS))) cmds=({});
+
+ if (!pointerp(cmd)) cmd=({cmd});
+
+ for (i = sizeof(cmd)-1; i>=0; i--)
+ cmds += ({({ cmd[i] , fun, flag, lvl})});
+
+ Set(P_LOCALCMDS, cmds);
+}
+
+static int auswerten(mixed cmd, string str)
+{
+ if (closurep(cmd))
+ return funcall(cmd,str);
+ if (stringp(cmd))
+ return call_other(this_object(),cmd,str);
+ return 0;
+}
+
+static varargs int __auswerten(string str, string intern)
+{
+ string verb;
+ mixed *cmd, cmds;
+ int i,ret,lvl,l,vl;
+
+ if (!intern)
+ verb=query_verb();
+ else
+ verb=intern;
+ lvl=query_wiz_level(ME);
+ vl=sizeof(verb);
+ cmds=QueryProp(P_LOCALCMDS);
+
+ for(i=sizeof(cmds)-1;i>=0;i--)
+ {
+ cmd=cmds[i],l=sizeof(cmd[0]);
+ if (cmd[0]==verb[0..l-1] && cmd[3]<=lvl && (cmd[2]||vl==l) &&
+ (ret=auswerten(cmd[1],str)))
+ return ret;
+ }
+ // An dieser Stelle gibt es hier und vermutlich nirgendwo anders etwas, was
+ // dieses Kommando als gueltig betrachtet. Wir informieren ggf. die
+ // Syntax-DB. (Achtung: wenn jemand ne add_action() im Spielerobjekt
+ // einbaut, die vor dieser eingetragen wird, ist die Annahme ggf. falsch.)
+ // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
+ // dorthin melden.
+ if (syntaxdb)
+ {
+ if (!objectp(syntaxdb[0]))
+ syntaxdb[0] = find_object("/secure/syntaxdb");
+ if (syntaxdb[0])
+ catch(syntaxdb[0]->cmd_unsuccessful();nolog);
+ }
+
+ return 0;
+}
+
+public void syntax_log_ep(int type)
+{
+ // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
+ // dorthin melden.
+ if (syntaxdb && syntaxdb[0])
+ {
+ catch(syntaxdb[0]->LogEP(type);nolog);
+ }
+}
+
+static mixed _query_localcmds()
+{
+ mixed *l;
+
+ l=Query(P_LOCALCMDS);
+ if (!pointerp(l))
+ l=({});
+ return ({
+ ({"ali","alias",0,0}),
+ ({"alias","alias",0,0}),
+ ({"unali","unalias",1,0}),
+ ({"histmin","histmin",0,0}),
+ ({"histlen","histlen",0,0}),
+ ({"hist","show_hist",0,0}),
+ ({"history","show_hist",0,0}),
+ ({"do","do_list",0,LEARNER_LVL}),
+ ({"ersetzungsanzeige","replacedisplay",0,0}),
+ ({"syntaxsammlung","collect_cmds",0,0}),
+ ({"fehlermeldung","set_errormessage",0,SEER_LVL}),
+ })+l;
+}
+
+static int collect_cmds(string cmd)
+{
+ if (!stringp(cmd))
+ {
+ _notify("Mit diesem Befehl kannst Du mithelfen, Syntaxen im MG zu "
+ "verbessern. Wenn Du einverstanden bist, speichern wir "
+ "anonym (d.h. ohne Deinen Charnamen), welche Deiner Befehle "
+ "erfolgreich und nicht erfolgreich waren. Uebliche "
+ "Kommunikationsbefehle werden dabei nicht gespeichert.",
+ 0);
+ _notify("Mit 'syntaxsammlung ja' kannst Du die Speicherung einschalten, "
+ "mit 'syntaxsammlung nein' kannst Du sie ausschalten.",0);
+ _notify("Deine Befehle werden zur Zeit"
+ + (QueryProp("_syntaxdb") ? " " : " NICHT ")
+ + "gespeichert.", 0);
+ }
+ else if (cmd == "ja")
+ {
+ SetProp("_syntaxdb", 1);
+ _notify("Ab jetzt werden Deine Befehle gespeichert. Vielen Dank!", 0);
+ }
+ else
+ {
+ SetProp("_syntaxdb", 0);
+ _notify("Ab jetzt werden Deine Befehle NICHT gespeichert.", 0);
+ }
+ return 1;
+}
+
+int _query_command_average()
+{
+ decay_average();
+ return cmds_per_time;
+}
+
+nomask void __set_bb(int flag) {
+ if( previous_object()!=find_object(BBMASTER) || process_call() )
+ return;
+ bb=flag;
+}
+
+
+nomask public void countCmds( int type, string key )
+{
+ string tmp;
+
+ if ( this_player() != this_interactive()
+ || this_interactive() != this_object()
+ || member( cmd_types, type ) < 0 )
+ return;
+
+ tmp = sprintf( "%d\n%s", type, key );
+
+ commands -= ({ tmp });
+ commands += ({ tmp });
+ commands = commands[0..max_commands-1];
+}
+
+
+nomask public string *getCmds()
+{
+ string *tmp;
+
+ if ( previous_object() != find_object(BBMASTER) )
+ return ({});
+
+ tmp = commands;
+ commands = ({});
+
+ return tmp;
+}
+
+/*
+ * Force the monster to do a command. The force_us() function isn't
+ * always good, because it checks the level of the caller, and this function
+ * can be called by a room.
+ */
+int command_me(string cmd)
+{
+ if (IS_LEARNER(ME))
+ {
+ if (!this_interactive() || !previous_object())
+ return 0;
+ if( geteuid(ME)!=geteuid(this_interactive())
+ || geteuid(ME)!=geteuid(previous_object()) )
+ {
+ if( query_wiz_level(ME)<query_wiz_level(previous_object()))
+ tell_object(ME,previous_object()->name()+" zwingt Dich zu: "
+ + cmd + ".\n");
+ else
+ {
+ tell_object(ME,previous_object()->name()
+ + " versucht, Dich zu " + cmd + " zu zwingen.\n" );
+ return 0;
+ }
+ }
+ }
+ return command(cmd);
+}
+
+
+static mixed _query_p_lib_disablecommands() {
+ // abgelaufen oder Objekt zerstoert? Weg damit.
+ if (pointerp(disablecommands)
+ && (disablecommands[B_TIME] < time()
+ || !objectp(disablecommands[B_OBJECT])) )
+ return(disablecommands = 0);
+
+ // sonst Kopie zurueck (copy(0) geht)
+ return(copy(disablecommands));
+}
+
+static mixed _set_p_lib_disablecommands(mixed data) {
+
+ // setzendes Objekt ermitteln, da diese Funktion nur per SetProp() gerufen
+ // werden sollte (!), ist das PO(1);
+ object origin = previous_object(1);
+ // wenn nicht existent, direkt abbruch
+ if (!objectp(origin))
+ return _query_p_lib_disablecommands();
+
+ // Prop loeschen? Explizit loeschen darf jeder, allerdings nicht direkt
+ // ungeprueft ueberschreiben.
+ if (!data) {
+ return (disablecommands = 0 );
+ }
+ // mal direkt buggen bei falschen Datentyp, damits auffaellt.
+ if (!pointerp(data) || sizeof(data) < 2 || !intp(data[0])
+ || (!stringp(data[1]) && !closurep(data[1]))
+ || (sizeof(data) >= 3 && !pointerp(data[2])) )
+ raise_error(sprintf(
+ "Wrong data type for P_DISABLE_COMMANDS. Expected Array with "
+ "2 or 3 elements (int, string|closure, [string*]), got %.25O\n",
+ data));
+
+ // Wenn abgelaufen oder gleiches Objekt wie letztes Mal: eintragen.
+ if (!disablecommands || (disablecommands[B_TIME] < time()
+ || !objectp(disablecommands[B_OBJECT])
+ || disablecommands[B_OBJECT] == origin) ) {
+ // Loggen nur, wenn eine Closure eingetragen wird. Reduziert den
+ // Logscroll und Strings haben deutlich weniger Missbrauchspotential.
+ if (closurep(data[1])) {
+ CBLOG(sprintf("[%s] CB gesetzt von %O, gueltig bis %s, Daten: %O\n",
+ strftime("%Y%m%d-%H:%M:%S"),origin,
+ strftime("%Y%m%d-%H:%M:%S",data[0]),
+ (stringp(data[1]) ? regreplace(data[1],"\n","\\n",0)
+ : data[1])));
+ }
+ if (sizeof(data)+1 <= B_EXCEPTIONS)
+ disablecommands = ({ origin, data[0], data[1], ({}) });
+ else
+ disablecommands = ({ origin, data[0], data[1], data[2] });
+ return(copy(disablecommands));
+ }
+
+ return(_query_p_lib_disablecommands());
+}
+
+static mixed _set__syntaxdb(mixed v)
+{
+ Set("_syntaxdb", v, F_VALUE);
+ if (v)
+ syntaxdb = ({find_object("/secure/syntaxdb")});
+ else
+ syntaxdb = 0;
+ return QueryProp("_syntaxdb");
+}
+
diff --git a/std/player/description.c b/std/player/description.c
new file mode 100644
index 0000000..4a0663f
--- /dev/null
+++ b/std/player/description.c
@@ -0,0 +1,292 @@
+// MorgenGrauen MUDlib
+//
+// player/description.c -- player description handling
+//
+// $Id: description.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/description";
+
+#include <thing/language.h>
+#include <player/description.h>
+#include <living/clothing.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <combat.h>
+#include <sys_debug.h>
+
+#define NEED_PROTOTYPES
+#include <player/command.h>
+
+protected void create()
+{
+ ::create();
+ Set(P_NAME, NOSETMETHOD, F_SET_METHOD);
+ Set(P_PRESAY, SAVE, F_MODE_AS);
+ Set(P_TITLE, SAVE, F_MODE_AS);
+ Set(P_EXTRA_LOOK, SAVE, F_MODE_AS);
+ Set(P_GENDER, SAVE, F_MODE_AS);
+ Set(P_SIZE, SAVE, F_MODE_AS);
+ Set(P_INFO, NOSETMETHOD, F_SET_METHOD);
+ // Avatar-URIs speichern. Ausserdem hat an denen keiner rumzufummeln.
+ Set(P_AVATAR_URI, SAVE|SECURED, F_MODE_AS);
+}
+
+string _query_info()
+{
+ string info;
+ info = Query(P_INFO);
+ if(!info)info=="";
+ info = (string)query_wiz_level(this_object());
+ if(IS_GOD(this_object()))
+ return info+" [Gott]\n";
+ if(IS_ARCH(this_object()))
+ return info+" [Erzmagier]\n";
+ if(IS_ELDER(this_object()))
+ return info+" [Weiser]\n";
+ if(IS_LORD(this_object()))
+ return info+" [Regionschef]\n";
+ if(IS_SPECIAL(this_object()))
+ return info+" [Special]\n";
+ if(IS_DOMAINMEMBER(this_object()))
+ return info+" [Regionsmitarbeiter]\n";
+ if(IS_WIZARD(this_object()))
+ return info+" [Magier]\n";
+ if(IS_LEARNER(this_object()))
+ return info+" [Magieranwaerter]\n";
+ info = QueryProp(P_LEVEL);
+ if(IS_SEER(this_object()))
+ return info+" [Seher]\n";
+ return info+" [Spieler]\n";
+}
+
+int _query_size() {
+ if (QueryProp(P_FROG))
+ return 20;
+ return Query(P_SIZE);
+}
+
+// short() -- get the short description of a player
+string short()
+{
+ string answer;
+ string title;
+
+ if(QueryProp(P_INVIS))
+ if(interactive(previous_object()) && IS_LEARNING(previous_object()))
+ return "("+QueryProp(P_NAME)+") \n";
+ else
+ return 0;
+
+ if(QueryProp(P_GHOST))
+ {
+ if (QueryProp(P_FROG))
+ return "Der Geist eines kleinen Frosches namens "+QueryProp(P_NAME)+
+ ".\n";
+ else
+ return "Der Geist von "+QueryProp(P_NAME)+".\n";
+ }
+
+ if (QueryProp(P_FROG))
+ return "Ein kleiner Frosch namens "+QueryProp(P_NAME)+".\n";
+
+ answer = QueryProp(P_PRESAY) + QueryProp(P_NAME);
+ if (QueryProp(P_ARTICLE)) answer=QueryArticle(0,0)+answer;
+ if((title=QueryProp(P_TITLE)) && title != "") answer += " " + title;
+ if(!interactive(ME)) answer += " (netztot)";
+ return answer+".\n";
+}
+
+private string andlist(object *arr) {
+ string *tmp;
+ if(!pointerp(arr)) return "";
+ if(sizeof(tmp = map_objects(arr, "name", WEN)))
+ return(CountUp(tmp));
+ return "";
+}
+
+// gibt fuer nicht-Invis-Objekte den Namen zurueck
+private string get_vis_name(object ob) {
+ if (ob->QueryProp(P_INVIS))
+ return 0;
+ return (ob->name(WEN));
+}
+
+varargs string long()
+{
+ string exl, descr, tmp, size_descr;
+ object ob;
+ mixed trans, w, a, r;
+ int per;
+ string fill, fill2;
+ /* fuer groessenvergleich */
+ string comparesize, pers1, pers2;
+ int relation;
+
+ per=1000*QueryProp(P_SIZE)/QueryProp(P_AVERAGE_SIZE);
+ switch(per)
+ {
+ case 0..800: size_descr="ziemlich winzig"; break;
+ case 801..850: size_descr="eher winzig"; break;
+ case 851..900: size_descr="recht klein"; break;
+ case 901..950: size_descr="eher klein"; break;
+ case 951..1050: size_descr="mittelgross"; break;
+ case 1051..1100: size_descr="relativ gross"; break;
+ case 1101..1150: size_descr="ziemlich gross"; break;
+ case 1151..1200: size_descr="sehr gross"; break;
+ default: size_descr="riesig"; break;
+ }
+#define RassenString() ((QueryProp(P_FROG) ? "en Frosch" :\
+ (!QueryProp(P_GENDER)?" ":QueryProp(P_GENDER)==2?"e ":"en ")+\
+ (pointerp(QueryProp(P_RACESTRING))?\
+ QueryProp(P_RACESTRING)[WEN]:QueryProp(P_RACE))))
+ fill2=fill=0;
+ if (QueryProp(P_AVERAGE_SIZE)<170)
+ {
+ if (per<950)
+ fill="selbst fuer ein"+RassenString()+" ";
+ else
+ if (per>1050)
+ fill2=", wenn auch nur fuer ein"+RassenString();
+ }
+ else
+ {
+ if (QueryProp(P_AVERAGE_SIZE)>170)
+ if (per>1050)
+ fill="sogar fuer ein"+RassenString()+" ";
+ else
+ if (per<950)
+ fill2=", wenn auch nur fuer ein"+RassenString();
+ }
+ if (!fill&&!fill2) fill="fuer ein"+RassenString()+" ";
+ descr = "Das ist "+name(WER,1)+". "+capitalize(QueryPronoun())+" ist "+
+ (fill||"")+size_descr+(fill2||"")+".\n";
+
+ if(this_player()) {
+ /* groessenvergleich_anfang (NEU) */
+ pers1 = QueryPronoun(WER);
+ pers2 = QueryPronoun(WEM);
+
+ // || falls TP keine Groesse gesetzt hat... Warum auch immer...
+ relation = (QueryProp(P_SIZE)*100) /
+ (this_player()->QueryProp(P_SIZE) || 1);
+ switch (relation)
+ {
+ case 0 .. 25 : comparesize = "Damit gibt "+pers1+" einen guten"
+ " Fusschemel fuer Dich ab";
+ break;
+ case 26 .. 50 : comparesize = "Damit reicht "+pers1+" Dir nicht mal bis"
+ " zur Huefte";
+ break;
+ case 51 .. 75 : comparesize = "Damit kannst Du "+pers2+" locker auf den"
+ " Kopf spucken";
+ break;
+ case 76 .. 90 : comparesize = "Damit ist "+pers1+" einen Kopf kleiner"
+ " als Du";
+ break;
+ case 91 .. 110 : comparesize = "Damit hat "+pers1+" etwa Deine Groesse";
+ break;
+ case 111 .. 120 : comparesize = "Damit ist "+pers1+" einen Kopf groesser"
+ " als Du";
+ break;
+ case 121 .. 150 : comparesize = "Damit holst Du Dir einen steifen Nacken,"
+ " wenn Du "+pers2+" in die Augen siehst";
+ break;
+ case 151 .. 200 : comparesize = "Damit versperrt "+pers1+" Dir absolut"
+ " die Sicht";
+ break;
+ case 201 .. 300 : comparesize = "Damit waere jeder Schlag von Dir ein"
+ " Tiefschlag";
+ break;
+ default : comparesize = "Damit kannst Du "+pers2+" bequem zwischen"
+ " den Beinen durchlaufen";
+ break;
+ }
+
+ descr+=comparesize+".\n";
+ /* groessenvergleich_ende (NEU) */
+ }
+
+ if(QueryProp(P_GHOST)) return descr;
+
+ trans = QueryProp(P_TRANSPARENT); SetProp(P_TRANSPARENT, 0);
+ descr += ::long(); SetProp(P_TRANSPARENT, trans);
+
+ // Waffen, Ruestungen/Kleidung und Sonstiges ermitteln
+ w = ({QueryProp(P_WEAPON), QueryProp(P_PARRY_WEAPON)}) - ({0});
+ a = QueryProp(P_ARMOURS) + QueryProp(P_CLOTHING) - ({0});
+ r = all_inventory(ME) - w - a; //Rest logischerweise
+
+ // rest noch nach sichbarkeit von ausserhalb des SPielers filtern.
+ r=map(r,function string (object ob) {
+ if(sizeof(all_inventory(ob)) || ob->QueryProp(P_LIGHT) ||
+ ob->QueryProp(P_LIGHTED) || ob->QueryProp(P_SHOW_INV))
+ return(get_vis_name(ob));
+ return(0); } ) - ({0});
+
+ // Invis-Objekte ausfiltern und Namen ermitteln, anschliessend sind in w, a
+ // und r je ein Array von Strings
+ w = map(w, #'get_vis_name) - ({0});
+ a = map(a, #'get_vis_name) - ({0});
+
+ return descr +
+ (QueryProp(P_TRANSPARENT) ?
+ break_string(capitalize(name(WER, 1))+" hat "
+ +(sizeof(a)?CountUp(a):"keine Ruestung")+" an"
+ +(sizeof(r)?", ":" und ")
+ +(sizeof(w)?CountUp(w):"keine Waffe")+" gezueckt"
+ +(sizeof(r)?" und traegt "+CountUp(r):"")+".", 78): "");
+}
+
+
+// **** local property methods
+static mixed _query_presay()
+{
+ string presay;
+ if((presay = Query(P_PRESAY)) && (presay != "")) return presay + " ";
+ return "";
+}
+
+static string _query_name()
+{
+ return capitalize(Query(P_NAME) || "NoName");
+}
+// ****
+
+// Local commands
+static mixed _query_localcmds() {
+ return
+ ({
+ ({"avatar","set_avataruri",0,0}),
+ });
+}
+
+int set_avataruri(string arg) {
+ arg = _unparsed_args(0);
+ if (!arg || !sizeof(arg)) {
+ string uri = QueryProp(P_AVATAR_URI);
+ if (stringp(uri))
+ tell_object(ME,
+ "Aktuelle Avatar-URI: " + uri + "\n");
+ else
+ tell_object(ME, "Du hast keine Avatar-URI eingestellt.\n");
+ }
+ else if (arg == "keine") {
+ SetProp(P_AVATAR_URI, 0);
+ tell_object(ME, "Deine Avatar-URI wurde geloescht.\n");
+ }
+ else if (sizeof(arg) > 512)
+ tell_object(ME, "Deine neue Avatar-URI ist zu lang!\n");
+ else {
+ SetProp(P_AVATAR_URI, arg);
+ tell_object(ME,
+ "Aktuelle Avatar-URI: " + arg + "\n");
+ }
+ return 1;
+}
+
diff --git a/std/player/exploration.c b/std/player/exploration.c
new file mode 100644
index 0000000..8a1976e
--- /dev/null
+++ b/std/player/exploration.c
@@ -0,0 +1,125 @@
+// MorgenGrauen MUDlib
+//
+// player/exploration.c -- exploration point management
+//
+// $Id: exploration.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player/life.h>
+#include <player/base.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <exploration.h>
+#include <scoremaster.h>
+#include <properties.h>
+#include <new_skills.h>
+
+private string given_scores;
+
+private nosave mixed epnum;
+
+void create() {
+ Set(P_LEP, SECURED|SAVE, F_MODE_AS);
+
+ given_scores = "";
+}
+
+string Forschung()
+{
+ return EPMASTER->QueryForschung();
+}
+
+static string _query_given_scores()
+{
+ return given_scores;
+}
+
+// Hier kommen Funktionen fuer die Levelpunkte
+
+#define XP_FAC ([1:10,2:40,3:150,4:600,5:2250,6:9000,7:35000,8:140000,9:500000])
+
+//#define DEBUG(x,y) printf(x,y)
+#define DEBUG(x,y)
+
+int AddScore(int contributor)
+{
+ mixed info;
+ object po;
+ int drin;
+
+ if (!pointerp(info = SCOREMASTER->QueryNPCbyNumber(contributor)))
+ return -1;
+
+ if ((po = previous_object()) && (object_name(po) == SCOREMASTER))
+ po = previous_object(1);
+
+ if (!po || old_explode(object_name(po),"#")[0] != info[SCORE_KEY])
+ return -2;
+
+ if (!stringp(given_scores))
+ given_scores = " ";
+
+ if (catch(drin = test_bit(given_scores, contributor);publish))
+ return -3;
+
+ if (!drin) {
+ given_scores = set_bit(given_scores, contributor);
+ Set(P_LEP, Query(P_LEP) + info[SCORE_SCORE]);
+ force_save();
+ return info[SCORE_SCORE];
+ }
+ return 0;
+}
+
+int TestScore(int contributor)
+{
+ int ret;
+
+ if (!previous_object() || (object_name(previous_object()) != SCOREMASTER))
+ return 0;
+
+ catch(ret = test_bit(given_scores, contributor);publish);
+
+ return ret;
+}
+
+int SetScoreBit(int contributor)
+{
+ int drin;
+
+ if (!previous_object() || (object_name(previous_object()) != SCOREMASTER))
+ return -1;
+
+ if (catch(drin = test_bit(given_scores, contributor);publish))
+ return -2;
+
+ if (drin) return -3;
+
+ given_scores = set_bit(given_scores, contributor);
+ force_save();
+ return 1;
+}
+
+int ClearScoreBit(int contributor)
+{
+ int drin;
+
+ if (!previous_object() || (object_name(previous_object()) != SCOREMASTER))
+ return -1;
+
+ if (catch(drin = test_bit(given_scores, contributor);publish))
+ return -2;
+
+ if (!drin) return -3;
+
+ given_scores = clear_bit(given_scores, contributor);
+ force_save();
+ return 1;
+}
+
diff --git a/std/player/guide.c b/std/player/guide.c
new file mode 100644
index 0000000..fb08b43
--- /dev/null
+++ b/std/player/guide.c
@@ -0,0 +1,182 @@
+// MorgenGrauen MUDlib
+//
+// player/guide.c -- newbie guide handling
+//
+// $Id: guide.c 7391 2010-01-25 22:52:51Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <config.h>
+#define NEED_PROTOTYPES
+#include <player/user_filter.h> // fuer is_active_guide()
+#include <properties.h>
+#include <defines.h>
+#include <thing/properties.h>
+
+/* Funktion, die die Guide-Kommandos aktiviert */
+void add_guide_commands()
+{
+ add_action("CiceroneCmd","cicerone");
+ // Sollte eigentlich an derselben Stelle moeglich sein.
+}
+
+/* Gibt die Meldung beim Aendern aus*/
+protected int NewbieChangeMsg() {
+ int cic=QueryProp(P_NEWBIE_GUIDE);
+ // begrenzen auf 1 Tag, falls jemand da Schrott reingschrieben hat.
+ if (cic > 86400) {
+ cic=86400;
+ SetProp(P_NEWBIE_GUIDE,cic);
+ }
+ if (cic<=0) {
+ write("Du bist jetzt kein Cicerone mehr.\n");
+ }
+ else if (cic < 60) {
+ write("Du bist jetzt ein Cicerone.\n");
+ }
+ else {
+ write(break_string(
+ "Du bist jetzt ein Cicerone, allerdings nur in den Zeiten, "
+ "in denen Du weniger als " + cic/60
+ + ((cic/60)<2 ? " Minute ":" Minuten ")
+ + "idle bist.\n",78));
+ }
+ return 1;
+}
+
+/* Gibt die Statusmeldung aus */
+protected int NewbieStatusMsg() {
+ int cic=QueryProp(P_NEWBIE_GUIDE);
+ // begrenzen auf 1 Tag, falls jemand da Schrott reingschrieben hat.
+ if (cic > 86400) {
+ cic=86400;
+ SetProp(P_NEWBIE_GUIDE,cic);
+ }
+
+ if (cic <= 0)
+ write ("Du bist kein Cicerone.\n");
+ else if (cic < 60)
+ write ("Du stehst Neuspielern als Cicerone zur Verfuegung.\n");
+ else {
+ write(break_string(
+ "Du stehst Neuspielern als Cicerone zur Verfuegung, allerdings "
+ "nur in den Zeiten, in denen Du weniger als " + cic/60
+ + ((cic/60)<2 ? " Minute ":" Minuten ")
+ + "idle bist.\n",78));
+ }
+ return 1;
+}
+
+/* Fuehrt das eigentliche Kommando aus*/
+
+int CiceroneCmd(string str)
+{
+ if (QueryProp(P_LEVEL)<20)
+ {
+ write(break_string("Du solltest erst noch ein wenig "
+ "Erfahrung sammeln, bevor Du Dich "
+ "als Cicerone zur Verfuegung stellst.",78));
+ return 1;
+ }
+ // Idlezeit uebergeben?
+ int idle=to_int(str);
+ // max. einen Tag (1440 min) zulassen.
+ if (idle < 0) idle=0;
+ else if (idle > 1440) idle=1440;
+
+ if (!str) {
+ return NewbieStatusMsg();
+ }
+ else if (str=="status") {
+ return NewbieStatusMsg();
+ }
+ // "ein" schaltet einfach generell ein, hierbei steht 1 in der Prop
+ // fuer "permanent ein".
+ else if (str=="ein") {
+ SetProp(P_NEWBIE_GUIDE,1);
+ return NewbieChangeMsg();
+ }
+ // "aus" oder "0" deaktiviert.
+ else if (str=="aus") {
+ SetProp(P_NEWBIE_GUIDE,0);
+ return NewbieChangeMsg();
+ }
+ // wenn Zahl uebergeben ist, die groesser 0 und kleiner 1440 ist
+ // (s.o.), wird es als Anzahl an Idle-Minuten aufgefasst, ab der man
+ // ausgeblendet werden will.
+ else if (idle) {
+ SetProp(P_NEWBIE_GUIDE, idle*60); // als Sekunden speichern.
+ return NewbieChangeMsg();
+ }
+ write(break_string(
+ "cicerone ein - Du bist Cicerone\n"
+ "cicerone aus - Du bist kein Cicerone\n"
+ "cicerone - Status anzeigen\n"
+ +break_string(
+ "Du bist Cicerone, aber wenn Du laenger als <zahl> Minuten "
+ "idle bist, wirst Du automatisch ausgeblendet, bis Du wieder "
+ "endidelt bist.",
+ 76,"cicerone zahl - ",BS_INDENT_ONCE),
+ 78,"Syntaxhilfe:",BS_PREPEND_INDENT|BS_LEAVE_MY_LFS));
+
+ return 1;
+}
+
+protected string IstSindMsg(string* namen)
+{
+ if (sizeof(namen)==1)
+ return "ist davon "+namen[0];
+ else
+ return "sind davon "+CountUp(namen);
+}
+
+void NewbieIntroMsg()
+{
+ object* cicerones;
+ string* namen;
+ string restext;
+
+ // Nur bis Level 5 wird etwas ausgegeben.
+ if (QueryProp(P_LEVEL)>5) return;
+
+ // is_active_guide() ist in /std/user_filter.c, welches vom
+ // Spielerobjekt geerbt wird und damit zur Verfuegung steht.
+ cicerones=filter(users(),#'is_active_guide);
+ // uid verwenden, da sonst kleine Spieler einen getarnten
+ // "Riesen" oder aehnliches anstprechen.
+ namen=map(cicerones,function string (object o)
+ { return(capitalize(geteuid(o))); } );
+
+ if (namen && sizeof(namen)>0)
+ {
+ restext="\nEs gibt einige nette Spieler, die bereit sind, Dich "
+ "auf Deinen ersten Schritten im "MUDNAME
+ " zu begleiten. \n\nDerzeit "
+ +IstSindMsg(namen)+" eingeloggt. Du kannst "
+ "einen oder eine von Ihnen ansprechen, "
+ "indem Du z.B. einfach \n"
+ " 'teile "+
+ lower_case(namen[random(sizeof(namen))])+
+ " mit Hallo ich bin neu hier, kannst Du "
+ "mir bitte helfen?'\n"
+ "eintippst. Nur keine Scheu, diese Spieler "
+ "haben sich freiwillig dazu bereiterklaert!\n"
+ "\nDu kannst Dir diese Spieler jederzeit "
+ "mit 'kwer cicerones' anzeigen lassen.\n\n";
+ write(break_string(restext,78,"* ",BS_LEAVE_MY_LFS));
+ }
+ else
+ {
+ // Weia, kein Newbie-Guide da. Lieber erstmal nix tun,
+ // bis uns was besseres einfaellt.
+ }
+ return;
+}
+
+
+
+
diff --git a/std/player/invmaster/gfx/Amulett_female b/std/player/invmaster/gfx/Amulett_female
new file mode 100644
index 0000000..5085026
--- /dev/null
+++ b/std/player/invmaster/gfx/Amulett_female
@@ -0,0 +1,5 @@
+6
+35
+5
+\_/
+?&
diff --git a/std/player/invmaster/gfx/Amulett_male b/std/player/invmaster/gfx/Amulett_male
new file mode 100644
index 0000000..224e09c
--- /dev/null
+++ b/std/player/invmaster/gfx/Amulett_male
@@ -0,0 +1,5 @@
+6
+36
+5
+\_/
+?&
diff --git a/std/player/invmaster/gfx/Beschriftung b/std/player/invmaster/gfx/Beschriftung
new file mode 100644
index 0000000..afddc5e
--- /dev/null
+++ b/std/player/invmaster/gfx/Beschriftung
@@ -0,0 +1,24 @@
+7
+0
+0
+??????????????????????????????????????????____________________<Helm>
+?<Waffe>______________
+??????????????????????\?????????????????????_____________<Koecher>
+
+??????????????????????????????????????????????_____________<Amulett>
+???????????????????????????????????????????????______
+?????????????????????????????????????????????????????\
+??????????????????????????????????????????????????????<Panzer>
+
+?<Ring>_____________/
+?????????????????????/??????????????????????????___________<Schild>
+?<Handschuhe>_______/??????????/
+??????????????????????????????/
+?<Guertel>___________________/
+
+?????????????????????????????????????????????\____________<Umhang>
+
+???????????????????????????????????????????_________<Hose>
+
+
+?????????<Schuhe>_____________
diff --git a/std/player/invmaster/gfx/Guertel_female b/std/player/invmaster/gfx/Guertel_female
new file mode 100644
index 0000000..83ed884
--- /dev/null
+++ b/std/player/invmaster/gfx/Guertel_female
@@ -0,0 +1,4 @@
+5
+33
+10
+`==O=='
diff --git a/std/player/invmaster/gfx/Guertel_male b/std/player/invmaster/gfx/Guertel_male
new file mode 100644
index 0000000..25c14a4
--- /dev/null
+++ b/std/player/invmaster/gfx/Guertel_male
@@ -0,0 +1,4 @@
+5
+33
+10
+`===O==='
diff --git a/std/player/invmaster/gfx/Handschuh_female b/std/player/invmaster/gfx/Handschuh_female
new file mode 100644
index 0000000..8e3f19f
--- /dev/null
+++ b/std/player/invmaster/gfx/Handschuh_female
@@ -0,0 +1,10 @@
+4
+21
+7
+?_,-_
+(__,I
+
+??????????????????????|7
+??????????????????????/|
+?????????????????????`)/
+??????????????????????'
diff --git a/std/player/invmaster/gfx/Handschuh_male b/std/player/invmaster/gfx/Handschuh_male
new file mode 100644
index 0000000..7b81dd1
--- /dev/null
+++ b/std/player/invmaster/gfx/Handschuh_male
@@ -0,0 +1,10 @@
+4
+22
+7
+,--__
+(__,I
+???????????????????????_?
+??????????????????????| |
+??????????????????????/ |
+??????????????????????`))
+?????????????????????? '
diff --git a/std/player/invmaster/gfx/Helm_female b/std/player/invmaster/gfx/Helm_female
new file mode 100644
index 0000000..63ba647
--- /dev/null
+++ b/std/player/invmaster/gfx/Helm_female
@@ -0,0 +1,6 @@
+1
+34
+0
+,-o-.
+====I
+???\_\
diff --git a/std/player/invmaster/gfx/Helm_male b/std/player/invmaster/gfx/Helm_male
new file mode 100644
index 0000000..2c188a3
--- /dev/null
+++ b/std/player/invmaster/gfx/Helm_male
@@ -0,0 +1,6 @@
+1
+35
+0
+,-o-.
+====I
+???\_\
diff --git a/std/player/invmaster/gfx/Hosen_female b/std/player/invmaster/gfx/Hosen_female
new file mode 100644
index 0000000..a9cfdd6
--- /dev/null
+++ b/std/player/invmaster/gfx/Hosen_female
@@ -0,0 +1,13 @@
+6
+32
+10
+/`-----'\
+|/ \|
+| Y |
+| | |
+?| | |
+?( | )
+?| | |
+?| | |
+?| | |
+?L_/ \_J
diff --git a/std/player/invmaster/gfx/Hosen_male b/std/player/invmaster/gfx/Hosen_male
new file mode 100644
index 0000000..e8e6390
--- /dev/null
+++ b/std/player/invmaster/gfx/Hosen_male
@@ -0,0 +1,13 @@
+6
+33
+10
+/`-----'\
+|/ \|
+| Y |
+| | |
+| | |
+<. | .>
+| | |
+| | |
+| | |
+?L_/ \_J
diff --git a/std/player/invmaster/gfx/Koecher_female b/std/player/invmaster/gfx/Koecher_female
new file mode 100644
index 0000000..de0b1cf
--- /dev/null
+++ b/std/player/invmaster/gfx/Koecher_female
@@ -0,0 +1,5 @@
+7
+40
+3
+"""
+//
diff --git a/std/player/invmaster/gfx/Koecher_male b/std/player/invmaster/gfx/Koecher_male
new file mode 100644
index 0000000..de0b1cf
--- /dev/null
+++ b/std/player/invmaster/gfx/Koecher_male
@@ -0,0 +1,5 @@
+7
+40
+3
+"""
+//
diff --git a/std/player/invmaster/gfx/Ring_female b/std/player/invmaster/gfx/Ring_female
new file mode 100644
index 0000000..00f8b00
--- /dev/null
+++ b/std/player/invmaster/gfx/Ring_female
@@ -0,0 +1,4 @@
+6
+21
+8
+c
diff --git a/std/player/invmaster/gfx/Ring_male b/std/player/invmaster/gfx/Ring_male
new file mode 100644
index 0000000..03620a8
--- /dev/null
+++ b/std/player/invmaster/gfx/Ring_male
@@ -0,0 +1,4 @@
+6
+22
+8
+c
diff --git a/std/player/invmaster/gfx/Ruestung_female b/std/player/invmaster/gfx/Ruestung_female
new file mode 100644
index 0000000..8849f35
--- /dev/null
+++ b/std/player/invmaster/gfx/Ruestung_female
@@ -0,0 +1,9 @@
+1
+26
+4
+??????___???___
+?????/( \_/ )\
+????/ |\_____/| \
+___/ /?\_/_\_/?\ \
+|__\'???)\_/(???\/|
+???????/_____\???||
diff --git a/std/player/invmaster/gfx/Ruestung_male b/std/player/invmaster/gfx/Ruestung_male
new file mode 100644
index 0000000..e59b67c
--- /dev/null
+++ b/std/player/invmaster/gfx/Ruestung_male
@@ -0,0 +1,9 @@
+1
+25
+4
+?????_^____???____^_?
+????( \ \_/ / )
+????/\ |\___ ___/| /\
+?__/ /?\_ _/?\_ >
+I___\>???)\___/(???| |
+????????/_______\??|_|
diff --git a/std/player/invmaster/gfx/Schild_female b/std/player/invmaster/gfx/Schild_female
new file mode 100644
index 0000000..fc49eea
--- /dev/null
+++ b/std/player/invmaster/gfx/Schild_female
@@ -0,0 +1,10 @@
+7
+43
+7
+??|\
+??||
+??||
+==||)
+??||
+??||
+??|/
diff --git a/std/player/invmaster/gfx/Schild_male b/std/player/invmaster/gfx/Schild_male
new file mode 100644
index 0000000..55b329f
--- /dev/null
+++ b/std/player/invmaster/gfx/Schild_male
@@ -0,0 +1,10 @@
+7
+45
+7
+??|\
+??||
+??||
+==||)
+??||
+??||
+??|/
diff --git a/std/player/invmaster/gfx/Schuhe_female b/std/player/invmaster/gfx/Schuhe_female
new file mode 100644
index 0000000..dbe958d
--- /dev/null
+++ b/std/player/invmaster/gfx/Schuhe_female
@@ -0,0 +1,7 @@
+5
+32
+18
+??_???_
+?|_|?|_|
+?/ J?L \
+(_/???\_)
diff --git a/std/player/invmaster/gfx/Schuhe_male b/std/player/invmaster/gfx/Schuhe_male
new file mode 100644
index 0000000..2b33014
--- /dev/null
+++ b/std/player/invmaster/gfx/Schuhe_male
@@ -0,0 +1,7 @@
+5
+32
+18
+???_???_
+??|_|?|_|
+?// J?L \\
+(__/???\__)
diff --git a/std/player/invmaster/gfx/Umhang_female b/std/player/invmaster/gfx/Umhang_female
new file mode 100644
index 0000000..2d885ba
--- /dev/null
+++ b/std/player/invmaster/gfx/Umhang_female
@@ -0,0 +1,17 @@
+2
+31
+4
+??_?????_
+??V?????V
+
+
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+?\???????/
diff --git a/std/player/invmaster/gfx/Umhang_male b/std/player/invmaster/gfx/Umhang_male
new file mode 100644
index 0000000..0025dbe
--- /dev/null
+++ b/std/player/invmaster/gfx/Umhang_male
@@ -0,0 +1,17 @@
+4
+31
+4
+???_?????_
+???V?????V
+
+
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+?\_???????_/
diff --git a/std/player/invmaster/gfx/axe b/std/player/invmaster/gfx/axe
new file mode 100644
index 0000000..9927f48
--- /dev/null
+++ b/std/player/invmaster/gfx/axe
@@ -0,0 +1,13 @@
+7
+19
+1
+?__????__
+// \()/ \\
+|| || ||
+\\_/||\_//
+????||
+????||
+
+
+????||
+????\/
diff --git a/std/player/invmaster/gfx/base_female b/std/player/invmaster/gfx/base_female
new file mode 100644
index 0000000..9eaec1a
--- /dev/null
+++ b/std/player/invmaster/gfx/base_female
@@ -0,0 +1,25 @@
+3
+0
+0
+
+ __
+ | \\
+ |_ /|
+ ___| ||__
+ / ` ' \
+ / |\_/ \_/| \
+ .-___,/ / \ / \ \
+ `--..__' ) ( \ |
+ / . \ ||
+ | \ / | /|
+ | Y | |'
+ \ | /
+ | | |
+ | | |
+ ( | )
+ | | | |
+ | ) ( |
+ | ) ( |
+ | | | |
+ / J L \
+ ''' ```
diff --git a/std/player/invmaster/gfx/base_male b/std/player/invmaster/gfx/base_male
new file mode 100644
index 0000000..b836653
--- /dev/null
+++ b/std/player/invmaster/gfx/base_male
@@ -0,0 +1,25 @@
+3
+0
+0
+
+ ___
+ | \\
+ |_ /|
+ ____| |____
+ /^ ` | ' ^\
+ / ,|\__/ \__/|.,\
+ ,--___| '/ \ ___ / \_`\
+ |_..___.' )/_|_\( | |
+ /\_|_/\ \ |
+ | .\ /. | ||
+ l| Y |l / |
+ || | || \|
+ |` '|` '|
+ | | |
+ ( | )
+ | | | |
+ | | |
+ | | |
+ | | | |
+ / J. .L \
+ ''' ```
diff --git a/std/player/invmaster/gfx/club b/std/player/invmaster/gfx/club
new file mode 100644
index 0000000..5566ece
--- /dev/null
+++ b/std/player/invmaster/gfx/club
@@ -0,0 +1,13 @@
+7
+21
+0
+??__
+_/__\_
+_|__|_
+_|__|_
+?\??/
+??||
+??||
+
+
+??()
diff --git a/std/player/invmaster/gfx/fernwaffe b/std/player/invmaster/gfx/fernwaffe
new file mode 100644
index 0000000..30897e8
--- /dev/null
+++ b/std/player/invmaster/gfx/fernwaffe
@@ -0,0 +1,19 @@
+7
+23
+0
+???)
+??/.
+?/?.
+|??.
+|??.
+|??.
+I??.
+
+
+I??.
+|??.
+|??.
+|??.
+?\?.
+??\.
+???)
diff --git a/std/player/invmaster/gfx/knife b/std/player/invmaster/gfx/knife
new file mode 100644
index 0000000..f6a4ecd
--- /dev/null
+++ b/std/player/invmaster/gfx/knife
@@ -0,0 +1,9 @@
+7
+23
+4
+?,
+(|
+(|
+--
+
+?U
diff --git a/std/player/invmaster/gfx/magic b/std/player/invmaster/gfx/magic
new file mode 100644
index 0000000..06c594b
--- /dev/null
+++ b/std/player/invmaster/gfx/magic
@@ -0,0 +1,10 @@
+7
+20
+0
+,
+?.?'??.
+?.\|/?,
+-?-+-?-
+,?/|\?.
+.?,.???.
+???\/
diff --git a/std/player/invmaster/gfx/misc b/std/player/invmaster/gfx/misc
new file mode 100644
index 0000000..74b5281
--- /dev/null
+++ b/std/player/invmaster/gfx/misc
@@ -0,0 +1,11 @@
+7
+23
+4
+/\
+||
+||
+
+
+||
+||
+`'
diff --git a/std/player/invmaster/gfx/spear b/std/player/invmaster/gfx/spear
new file mode 100644
index 0000000..4ae9d6c
--- /dev/null
+++ b/std/player/invmaster/gfx/spear
@@ -0,0 +1,25 @@
+7
+23
+0
+/\
+!|
+\/
+||
+||
+||
+||
+
+
+||
+||
+||
+||
+||
+||
+||
+||
+||
+||
+||
+||
+`'
diff --git a/std/player/invmaster/gfx/staff b/std/player/invmaster/gfx/staff
new file mode 100644
index 0000000..58446d0
--- /dev/null
+++ b/std/player/invmaster/gfx/staff
@@ -0,0 +1,22 @@
+7
+23
+0
+,.
+||
+||
+||
+||
+||
+||
+
+
+||
+||
+||
+||
+||
+||
+||
+||
+||
+`'
diff --git a/std/player/invmaster/gfx/sword b/std/player/invmaster/gfx/sword
new file mode 100644
index 0000000..e0ff2dc
--- /dev/null
+++ b/std/player/invmaster/gfx/sword
@@ -0,0 +1,13 @@
+7
+22
+0
+??,
+?||
+?||
+?||
+?||
+?||
+_||_
+
+
+?`'
diff --git a/std/player/invmaster/gfx/whip b/std/player/invmaster/gfx/whip
new file mode 100644
index 0000000..00db843
--- /dev/null
+++ b/std/player/invmaster/gfx/whip
@@ -0,0 +1,21 @@
+7
+16
+2
+?? ,_
+? / \
+ / ?? \
+ | ??? |
+ | ??? ^
+ |
+? \
+?? | ??#
+?? /
+? /
+? |
+? /
+ /
+ |
+ |
+ |
+ \
+? `
diff --git a/std/player/invmaster/invmaster.c b/std/player/invmaster/invmaster.c
new file mode 100644
index 0000000..06ccda0
--- /dev/null
+++ b/std/player/invmaster/invmaster.c
@@ -0,0 +1,464 @@
+// invmaster.c by Nachtwind@MG V1.1 (5.3.2001)
+// A small master that provides a graphical display of the player´s
+// equipment.
+#pragma strong_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <input_to.h>
+#include <properties.h>
+#include <ansi.h>
+#include <combat.h>
+#include <language.h>
+#include "invmaster.h"
+
+
+mapping data;
+closure abbreviate;
+
+
+// i'm aware this can be determined with m_indices(VALID_ARMOUR_TYPE),
+// but the position in the arrays is important for the drawing order.
+// first item in the array is drawn last
+static string *armour_types =
+({AT_BELT,
+ AT_SHIELD,
+ AT_HELMET,
+ AT_BOOT,
+ AT_TROUSERS,
+ AT_AMULET,
+ AT_RING,
+ AT_GLOVE,
+ AT_QUIVER,
+ AT_CLOAK,
+ AT_ARMOUR});
+
+static mapping colors =
+([0:ANSI_BLACK,
+ 1:ANSI_RED,
+ 2:ANSI_GREEN,
+ 3:ANSI_YELLOW,
+ 4:ANSI_BLUE,
+ 5:ANSI_PURPLE,
+ 6:ANSI_CYAN,
+ 7:ANSI_WHITE,
+ 8:""]);
+
+static mapping bgcolors =
+([0:ANSI_BG_BLACK,
+ 1:ANSI_BG_RED,
+ 2:ANSI_BG_GREEN,
+ 3:ANSI_BG_YELLOW,
+ 4:ANSI_BG_BLUE,
+ 5:ANSI_BG_PURPLE,
+ 6:ANSI_BG_CYAN,
+ 7:ANSI_BG_WHITE,
+ 8:""]);
+
+
+
+static string Mapping2ColoredText(mapping pic, object player);
+static string Mapping2PlainText(mapping pic);
+static void AddDescription(mapping pic, string type, object item);
+static string ComposeDesc(object item);
+static void ConfigureColors(string text);
+
+void ShowInv(object player, string arg);
+
+// ok, let´s just read in the graphics...
+// really takes some time (~250 eval ticks) but is only done
+// once in an uptime
+void create()
+{
+ mapping pic;
+ string *files, *lines, text;
+ int i,j,k, indentx,indenty, color;
+
+ data=([]);
+
+ DB("Trying to fire up master, path is '"+INVPATH+"'...");
+ files=get_dir(INVPATH+"gfx/*")-({".", ".."});
+ DB(sprintf("Files found in 'gfx/': \n%O", files));
+ for (i=sizeof(files)-1;i>=0;i--)
+ {
+ DB("Reading '"+files[i]+"' ...");
+ text=read_file(INVPATH+"gfx/"+files[i]);
+ if (!stringp(text))
+ {
+ DB("Failed to read file.");
+ continue;
+ }
+ lines=explode(text, "\n");
+ if (sizeof(lines) < 4)
+ {
+ DB("File corrupt.");
+ continue;
+ }
+ indentx=to_int(lines[1]);
+ indenty=to_int(lines[2]);
+ color=to_int(lines[0]);
+ pic=([]);
+ for (j=sizeof(lines)-1;j>2;j--)
+ {
+ for (k=sizeof(lines[j])-1;k>=0;k--)
+ {
+ if (lines[j][k..k]!="?")
+ pic+=([(j-3+indenty)*80+k+indentx:lines[j][k..k];color]);
+ }
+ }
+ data+=([files[i]:pic]);
+ DB("File successfully read.");
+ }
+ DB(sprintf("Types covered:\n%O\n", m_indices(data)));
+
+ // create closure only once to save time
+ // needed by ComposeDesc()
+ // the closure ist not as complicated as it seems ;)
+ // it just checks every word of the name, if it does not begin
+ // with a capital letter, it is abbreviated
+ // this happens only if the name length exceeds 20 chars...
+ abbreviate=lambda(({'x}),
+ ({#'?, ({#'>, ({#'member, quote(({"der", "des"})), 'x}), 0}),
+ "d.",
+ ({#'?, ({#'>, ({#'sizeof, 'x}), 3}),
+ ({#'?, ({#',, ({#'=, 'a, ({#'allocate, 1}) }),
+ ({#'=, ({#'[, 'a, 0}), 'x }),
+ ({#'sizeof, ({#'regexp, 'a, "^[a-z].*"}) })
+ }),
+ ({#'+, ({#'[..], 'x, 0, 1}), "."}),
+ 'x
+ }),
+ 'x
+ })
+ }));
+}
+
+// function that tries to guess a good item name and use abbrevations
+// where possible
+static string ComposeDesc(object item)
+{
+ int i;
+ string text, *buff;
+
+ text=regreplace(item->QueryProp(P_SHORT)
+ ||item->QueryProp(P_NAME)
+ ||"<?>",
+ "^(Ein Paar|Ein|Eine|Der|Die|Das) ","",0);
+
+// try to shorten the name with the closure
+ if (sizeof(text) > 20)
+ return implode(map(explode(text, " "), abbreviate), " ");
+ else
+ return text;
+}
+
+// converts a mapping with characters and color info into a
+// text. data in the mapping is stored in a one-dimensional
+// order with the position as key (ypos is pos/80, xpos pos%80)
+// this setup has a huge advantage: combining several
+// graphics just takes a "+" operator, the rest is handled
+// by the game driver. freakin' fast, much better than doing an
+// iteration over one or more array in lpc.
+static string Mapping2ColoredText(mapping pic, object player)
+{
+ string text;
+ mapping configmap;
+ int i,j,color;
+
+ configmap=default_config+(player->QueryProp(P_INVMASTER_CONFIG)||([]));
+
+ text="";
+ color=0;
+ for (i=0;i<22;i++)
+ {
+ text+=bgcolors[configmap[8]];
+ for (j=0;j<78;j++)
+ {
+ if (pic[i*80+j,1]!=color)
+ {
+ color=pic[i*80+j,1];
+ text+=colors[configmap[color]];
+ }
+ text+=pic[i*80+j];
+ }
+ text+=ANSI_NORMAL+"\n";
+ color=0;
+ }
+ return text;
+}
+
+static string Mapping2PlainText(mapping pic)
+{
+ string text;
+ int i,j;
+
+ text="";
+
+ for (i=0;i<22;i++)
+ {
+ for (j=0;j<78;j++)
+ text+=pic[i*80+j];
+ text+="\n";
+ }
+ return text;
+}
+static void AddDescription(mapping pic, string type, object item)
+{
+ int indentx, indenty, i;
+ string text;
+
+ switch(type)
+ {
+ case AT_HELMET:
+ indentx=47;
+ indenty=0;
+ text=sprintf("%-30s",ComposeDesc(item)[0..30]);break;
+ case AT_QUIVER:
+ indentx=49;
+ indenty=2;
+ text=sprintf("%-28s",ComposeDesc(item)[0..28]);break;
+ case AT_AMULET:
+ indentx=49;
+ indenty=4;
+ text=sprintf("%-27s",ComposeDesc(item)[0..28]);break;
+ case AT_ARMOUR:
+ indentx=53;
+ indenty=7;
+ text=sprintf("%-24s",ComposeDesc(item)[0..25]);break;
+ case AT_SHIELD:
+ indentx=54;
+ indenty=10;
+ text=sprintf("%-20s",ComposeDesc(item)[0..24]);break;
+ case AT_CLOAK:
+ indentx=53;
+ indenty=15;
+ text=sprintf("%-20s",ComposeDesc(item)[0..25]);break;
+ case AT_TROUSERS:
+ indentx=49;
+ indenty=17;
+ text=sprintf("%-20s",ComposeDesc(item)[0..20]);break;
+ case AT_RING:
+ indentx=0;
+ indenty=9;
+ text=sprintf("%14s",ComposeDesc(item)[0..17]);break;
+ case AT_GLOVE:
+ indentx=0;
+ indenty=11;
+ text=sprintf("%14s",ComposeDesc(item)[0..17]);break;
+ case AT_BELT:
+ indentx=1;
+ indenty=13;
+ text=sprintf("%14s",ComposeDesc(item)[0..18]);break;
+ case AT_BOOT:
+ indentx=1;
+ indenty=20;
+ text=sprintf("%18s",ComposeDesc(item)[0..18]);break;
+ case "Waffe":
+ indentx=1;
+ indenty=1;
+ text=sprintf("%18s",ComposeDesc(item)[0..25]);
+ if (item->QueryProp(P_NR_HANDS) > 1 &&
+ this_player() &&
+ !(this_player()->QueryArmorByType(AT_SHIELD)))
+ AddDescription(pic, AT_SHIELD, item);break;
+ default: return;
+ }
+ for (i=0;i<sizeof(text);i++)
+ pic+=([(80*indenty+indentx+i):text[i..i];2]);
+}
+
+varargs static void ConfigureColors(string text)
+{
+ mapping config, display;
+ string *strs;
+
+ if (!objectp(this_player())) return;
+
+ if (this_player()->InFight())
+ {
+ write(break_string(
+ "Im Kampf? Na Du hast Nerven, das lassen wir doch mal lieber! "
+ "Probier es danach nochmal...", 78));
+ return;
+ }
+
+ if (stringp(text)) text=lower_case(text);
+
+ if (text=="ok")
+ {
+ write("Farbkonfiguration beendet.\n");
+ return;
+ }
+
+ //"ansi_config", def in invmaster.h
+ config=this_player()->QueryProp(P_INVMASTER_CONFIG)||([]);
+ display=default_config+config;
+
+ if (!text || text=="")
+ {
+ write(
+ "*** Farbkonfiguration fuer den Ausruestungsbefehl ***\n\n"
+ " Farbe: wird dargestellt mit:\n"
+ "------------------ --------------------\n"
+ " Hintergrund "+COLORNAMES[display[8]]+"\n"
+ " Schwarz "+COLORNAMES[display[0]]+"\n"
+ " Rot "+COLORNAMES[display[1]]+"\n"
+ " Gruen "+COLORNAMES[display[2]]+"\n"
+ " Gelb "+COLORNAMES[display[3]]+"\n"
+ " Blau "+COLORNAMES[display[4]]+"\n"
+ " Magenta "+COLORNAMES[display[5]]+"\n"
+ " Tuerkis "+COLORNAMES[display[6]]+"\n"
+ " Weiss "+COLORNAMES[display[7]]+"\n\n"
+ "Farbe aendern mit '<farbe> <gewuenschte farbe>'.\n"
+ "Beispiel: 'gelb rot'.\n"
+ "Alles, was standardmaessig gelb waere, wuerde dann mit der ANSI-Farbe \n"
+ "Rot dargestellt.\n"
+ "Der Hintergrund kann zusaetzlich die Farbe 'keine' haben, bei der der \n"
+ "Hintergrund eben ueberhaupt nicht gefaerbt wird.\n"
+ "Beispiel: 'hintergrund keine'. Schaltet die Hintergrundfarbe aus.\n\n"
+ "Beenden mit 'ok'. \n"
+ "Wiederholung der Farbliste mit <Return>.\n"
+ "Farbliste auf Standard zuruecksetzen mit 'reset'.\n");
+ }
+ else
+ if (text=="reset")
+ {
+ this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AD);
+ this_player()->SetProp(P_INVMASTER_CONFIG, 0);
+ write("Farben zurueckgesetzt!\n");
+ }
+ else
+ {
+ if ( sizeof(strs=explode(text, " ")-({""})) !=2
+ || !member((COLORCODES-(["keine"])), strs[0])
+ || !member((COLORCODES-(["hintergrund"])), strs[1])
+ || ((strs[0]!="hintergrund") && (strs[1]=="keine")) )
+ {
+ write("Falsche Eingabe.\n"
+ "Format: <farbe|hintergrund> <zugewiesene Farbe>\n"
+ "Abbrechen mit 'ok'.\n");
+ }
+ else
+ {
+ if (COLORCODES[strs[1]]==default_config[COLORCODES[strs[0]]])
+ config-=([COLORCODES[strs[0]]]);
+ else
+ config+=([COLORCODES[strs[0]]:COLORCODES[strs[1]]]);
+ if (!sizeof(config))
+ {
+ this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AD);
+ this_player()->SetProp(P_INVMASTER_CONFIG, 0);
+ }
+ else
+ {
+ this_player()->SetProp(P_INVMASTER_CONFIG, deep_copy(config));
+ this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AS);
+ }
+ write("Ok, Farbe gewaehlt!\n");
+ }
+ }
+ input_to("ConfigureColors", INPUT_PROMPT, "\nEingabe: ");
+}
+
+
+string* armour_order=({
+ AT_HELMET, AT_AMULET, AT_QUIVER, AT_ARMOUR, AT_CLOAK,
+ AT_GLOVE, AT_RING, AT_BELT,
+ AT_TROUSERS, AT_BOOT, AT_SHIELD, AT_MISC});
+
+mapping weapon_names=([
+ WT_SPEAR : "Speer",
+ WT_SWORD : "Schwert",
+ WT_STAFF : "Kampfstab",
+ WT_WHIP : "Peitsche",
+ WT_CLUB : "Keule",
+ WT_KNIFE : "Messer",
+ WT_MISC : "Irgendwas",
+ WT_MAGIC : "Artefakt",
+ WT_AXE : "Axt",
+ WT_RANGED_WEAPON : "Fernwaffe"
+ ]);
+
+string SimpleInv(object player) {
+ object* armours=player->QueryProp(P_ARMOURS);
+ int count=sizeof(armour_order);
+ string* list=allocate(count);
+ string result="Ausruestung\n";
+ int i;
+
+ foreach(object ob: armours) {
+ if (!objectp(ob)) continue;
+ int idx = member(armour_order, ob->QueryProp(P_ARMOUR_TYPE));
+ if (idx>=0)
+ list[idx]=ob->QueryProp(P_SHORT);
+ }
+
+ // AT_MISC (letztes Element in list und armour_order) weglassen.
+ for (i=0;i<count-1;i++) {
+ result+=sprintf("%-20s %-57s\n",armour_order[i],list[i] || "");
+ }
+
+ object ob=ob=player->QueryProp(P_WEAPON);
+ if (objectp(ob)) {
+ result+=sprintf("%-20s %-57s\n",
+ (ob->QueryProp(P_NR_HANDS)==1 ? "Einhand-":"Zweihand-")
+ +weapon_names[ob->QueryProp(P_WEAPON_TYPE)],
+ ob->QueryProp(P_SHORT));
+ } else result+="Keine Waffe\n";
+
+ return result;
+}
+// the main function called by the player object.
+// determines gender, then adds armor/weapon graphics
+// dynamically. still very fast due to the use of the "+" operator,
+// see above.
+void ShowInv(object player, string arg)
+{
+ string gender, type;
+ mapping pic;
+ int i;
+ object item;
+
+ if (!objectp(player)||!interactive(player)) return;
+
+ // split args.
+ string *args;
+ if (stringp(arg))
+ args = explode(lower_case(arg), " ") - ({" "});
+ else
+ args = ({});
+
+ if (member(args, "farben") > -1) {
+ ConfigureColors();
+ return;
+ }
+
+ if (member(args, "-k") > -1 || player->QueryProp(P_NO_ASCII_ART)) {
+ tell_object(player, SimpleInv(player));
+ return;
+ }
+
+ gender=player->QueryProp(P_GENDER)==FEMALE?"_female":"_male";
+ pic=deep_copy(data["base"+gender]);
+ pic+=data["Beschriftung"];
+ for (i=sizeof(armour_types)-1;i>=0;i--)
+ if (objectp(item=player->QueryArmourByType(armour_types[i])))
+ {
+ pic+=data[armour_types[i]+gender];
+ AddDescription(pic, armour_types[i], item);
+ }
+ if (item=player->QueryProp(P_WEAPON))
+ {
+ pic+=data[(VALID_WEAPON_TYPE(type=item->QueryProp(P_WEAPON_TYPE)))?
+ type:WT_MISC];
+ AddDescription(pic, "Waffe", item);
+ }
+ if (player->QueryProp(P_TTY)!="ansi")
+ player->More(Mapping2PlainText(pic));
+ else
+ player->More(Mapping2ColoredText(pic, player));
+ DB(geteuid(player)+" eval cost: "+(1000000-get_eval_cost())+" ticks.\n");
+}
+
diff --git a/std/player/invmaster/invmaster.h b/std/player/invmaster/invmaster.h
new file mode 100644
index 0000000..8821b63
--- /dev/null
+++ b/std/player/invmaster/invmaster.h
@@ -0,0 +1,21 @@
+#define INVPATH "/std/player/invmaster/"
+#define DB(x)
+/*
+#define DB(x) if (find_player("rikus")) \
+ tell_object(find_player("rikus"), \
+ break_string(x, 78, "INVMASTER: ", 1));
+*/
+#define P_INVMASTER_CONFIG "invmaster_config"
+#define COLORNAMES ({"Schwarz","Rot","Gruen","Gelb",\
+ "Blau","Magenta","Tuerkis","Weiss","Keine"})
+#define COLORCODES (["schwarz":0,\
+ "rot":1,\
+ "gruen":2,\
+ "gelb":3,\
+ "blau":4,\
+ "magenta":5,\
+ "tuerkis":6,\
+ "weiss":7,\
+ "keine":8,\
+ "hintergrund":8])
+#define default_config ([0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:0])
diff --git a/std/player/life.c b/std/player/life.c
new file mode 100644
index 0000000..73f1ff7
--- /dev/null
+++ b/std/player/life.c
@@ -0,0 +1,841 @@
+// MorgenGrauen MUDlib
+//
+// player/life.c -- player life handling
+//
+// $Id: life.c 9397 2015-12-11 20:29:26Z Zesstra $
+
+// Defines some things which are different than in living.c
+// One example is the heart_beat().
+
+// Properties
+// P_AGE -- Age
+
+#pragma strong_types, save_types, rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/life";
+
+#include <rtlimits.h>
+#include <debug_info.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <player/base.h>
+#include <player/life.h>
+#include <moving.h>
+#include <player/comm.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <language.h>
+#include <properties.h>
+#include <hook.h>
+
+#include <living/life.h>
+#include <player/pklog.h>
+#include <player/combat.h>
+#include <health.h>
+#include <living/combat.h>
+#include <attributes.h>
+#include <defuel.h>
+#include <new_skills.h>
+
+// Die Folgen eines Todes wirken 4 Stunden lang nach
+#define SUFFER_TIME 7200
+#define P_DEATH_INFO "death_info"
+
+int age; /* Number of heart beats of this character. */
+
+private int suffer_time;
+static int time_to_save; /* when to next save player */
+private nosave int die_in_progress; // semaphore fuer die()-Aufrufe.
+
+static int _set_playerkills( int val );
+
+protected void create()
+{
+ ::create();
+ Set(P_AGE, -1, F_SET_METHOD);
+ Set(P_AGE, PROTECTED, F_MODE);
+ Set( P_KILLS, SAVE|SECURED, F_MODE_AS );
+ Set( P_GHOST, SAVE, F_MODE_AS );
+ Set( P_TIMING_MAP, SAVE|SECURED, F_MODE_AS );
+ Set( P_LAST_DEATH_TIME, SAVE|SECURED, F_MODE_AS );
+ Set( P_DEATH_INFO, SAVE|SECURED, F_MODE_AS );
+ Set( P_DEFUEL_LIMIT_FOOD,PROTECTED,F_MODE_AS);
+ Set( P_DEFUEL_LIMIT_DRINK,PROTECTED,F_MODE_AS);
+ Set( P_DEFUEL_TIME_FOOD,PROTECTED,F_MODE_AS);
+ Set( P_DEFUEL_TIME_DRINK,PROTECTED,F_MODE_AS);
+ Set( P_DEFUEL_AMOUNT_FOOD,PROTECTED,F_MODE_AS);
+ Set( P_DEFUEL_AMOUNT_DRINK,PROTECTED,F_MODE_AS);
+ offerHook(H_HOOK_HP,1);
+ offerHook(H_HOOK_SP,1);
+ // P_TIMING_MAP aufraeumen, aber zeitverzoegert, weil jetzt die Daten noch
+ // nicht aus dem Savefile geladen wurden.
+ call_out(#'expire_timing_map, 4);
+}
+
+// called from base.c in Reconnect()
+protected void reconnect() {
+ expire_timing_map();
+}
+
+static int _set_playerkills( int val )
+{
+ string tmp;
+ int playerkills;
+
+ // ist der Setzer in einer Arena/Schattenwelt. Dann nicht. (Ja, Bug ist,
+ // dass EMs aus Schattenwelt/Arena heraus auch das PK-Flag nicht
+ // zuruecksetzen koennen.)
+ if ( previous_object(1) && environment(previous_object(1)) &&
+ (tmp = object_name(environment(previous_object(1)))) &&
+ CheckArenaFight(previous_object(1)) )
+ return 0;
+
+ tmp = sprintf( "%O: %s %O %s",
+ previous_object(1) || this_interactive() || this_player(),
+ getuid(ME), val, dtime(time()) );
+
+ playerkills = Query(P_KILLS);
+
+ if( intp(val) && val >= 0 )
+ if( previous_object(1) && IS_ARCH(getuid(previous_object(1))) &&
+ this_interactive() && IS_ARCH(this_interactive()) )
+ playerkills = val;
+ else
+ tmp += " ILLEGAL!";
+ else
+ playerkills++;
+
+ if ( !previous_object(1) || !query_once_interactive(previous_object(1)) ||
+ IS_LEARNER(previous_object(1)) )
+ log_file( "SET_KILLS", tmp + "\n" );
+
+ return Set( P_KILLS, playerkills );
+}
+
+
+public int death_suffering()
+{
+ if ( suffer_time <= 0 )
+ return suffer_time = 0;
+
+ return 1 + (10 * suffer_time) / SUFFER_TIME;
+}
+
+
+protected void heart_beat()
+{
+ mapping di, mods;
+
+ ++age; // erstmal altern ;-)
+
+ ::heart_beat();
+
+ if ( age > time_to_save ){
+ save_me(1);
+ time_to_save = age + 500;
+ }
+
+ // als geist hat man mit den meisten weltlichen Dingen nicht allzuviel zu
+ // tun.
+ if ( QueryProp(P_GHOST) )
+ return;
+
+ if ( suffer_time > 0 )
+ suffer_time--;
+
+ // Todesfolgen nur alle 20 Sekunden (10 HB) checken.
+ // Das ist immer noch oft genug und spart Rechenzeit.
+ if ( (age % 10) || !mappingp(di = QueryProp(P_DEATH_INFO)) )
+ return;
+
+ mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"];
+ if (!mappingp(mods)) return;
+
+ if ( mods[A_STR] && --di[A_STR] <= 0) {
+ // einen Attributspunkt regenerieren
+ if ( mods[A_STR] < -1 ) {
+ mods[A_STR]++;
+ di[A_STR] = (110 + 5 * (di[A_STR, 1] + mods[A_STR])) / 10;
+ }
+ else {
+ m_delete( mods, A_STR );
+ m_delete( di, A_STR );
+ }
+ }
+
+ if ( mods[A_CON] && --di[A_CON] <= 0) {
+ // einen Attributspunkt regenerieren
+ if ( mods[A_CON] < -1 ){
+ mods[A_CON]++;
+ di[A_CON] = (110 + 5 * (di[A_CON, 1] + mods[A_CON])) / 10;
+ }
+ else {
+ m_delete( mods, A_CON );
+ m_delete( di, A_CON );
+ }
+ }
+
+ if ( mods[A_DEX] && --di[A_DEX] <= 0) {
+ // einen Attributspunkt regenerieren
+ if ( mods[A_DEX] < -1 ){
+ mods[A_DEX]++;
+ di[A_DEX] = (110 + 5 * (di[A_DEX, 1] + mods[A_DEX])) / 10;
+ }
+ else {
+ m_delete( mods, A_DEX );
+ m_delete( di, A_DEX );
+ }
+ }
+
+ if ( mods[A_INT] && --di[A_INT] <= 0) {
+ // einen Attributspunkt regenerieren
+ if ( mods[A_INT] < -1 ){
+ mods[A_INT]++;
+ di[A_INT] = (110 + 5 * (di[A_INT, 1] + mods[A_INT])) / 10;
+ }
+ else {
+ m_delete( mods, A_INT );
+ m_delete( di, A_INT );
+ }
+ }
+
+ if ( sizeof(di) && sizeof(mods))
+ SetProp( P_DEATH_INFO, di );
+ else
+ SetProp( P_DEATH_INFO, 0 );
+
+ SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+}
+
+
+public void force_save() {
+ time_to_save = 0;
+}
+
+
+nomask public int do_damage( int dam, object enemy )
+{
+ int hit_point;
+
+ if( QueryProp(P_GHOST) || dam <= 0 )
+ return 0;
+
+ hit_point = QueryProp(P_HP);
+
+ if ( query_once_interactive(ME) && dam >= hit_point && IS_LEARNING(ME) ){
+ tell_object( ME, "Deine magischen Kraefte verhindern Deinen Tod.\n" );
+ return 0;
+ }
+
+ if ( !objectp(enemy) )
+ enemy = previous_object() || this_interactive() || this_player();
+
+ hit_point -= dam;
+
+ if( hit_point < 0 ){
+ if ( !interactive(ME) )
+ // Netztote sterben nicht
+ hit_point = 10;
+ else {
+ if ( objectp(enemy) && interactive( enemy ) && enemy != ME &&
+ !QueryProp(P_TESTPLAYER) && !IS_WIZARD(ME) &&
+ !CheckArenaFight(ME) ) {
+ if ( QueryPlAttacked(enemy) )
+ hit_point = 1;
+ else {
+ hit_point = 0;
+ enemy->SetProp( P_KILLS, -1 );
+ }
+
+ log_file( "KILLER",
+ sprintf( "%s %s(%d/%d) toetete %s(%d/%d)%s\n",
+ ctime(time()),
+ getuid(enemy), query_wiz_level(enemy),
+ (int) enemy->QueryProp(P_LEVEL), getuid(ME),
+ query_wiz_level(ME), QueryProp(P_LEVEL),
+ (hit_point ? " NOTWEHR=>KEIN PK" : "") ) );
+ }
+ else {
+ string killername;
+ if (objectp(enemy))
+ killername=sprintf("%s (%s)",
+ BLUE_NAME(enemy), REAL_UID(enemy));
+ else
+ killername="??? (???)";
+
+ if ( !QueryProp(P_TESTPLAYER) )
+ create_kill_log_entry(killername, enemy );
+ }
+
+ if ( enemy )
+ enemy->StopHuntFor( ME, 1 );
+
+ map_objects( QueryEnemies()[0], "StopHuntFor", ME, 1 );
+ StopHuntingMode(1);
+
+ Set( P_KILLER, enemy );
+ die();
+ }
+ }
+
+ SetProp( P_HP, hit_point );
+ return dam;
+}
+
+
+// Loescht im sterbenden Spieler die 'koerperabhaengigen' Properties
+private void reset_my_properties()
+{
+ // Loeschen der Properties
+ if ( QueryProp(P_POISON) )
+ {
+ // Don't die twice 'cause of the same poison
+ Set( P_POISON, 0, F_SET_METHOD );
+ Set( P_POISON, 0, F_QUERY_METHOD );
+ SetProp( P_POISON, 0 );
+ }
+
+ Set( P_FROG, 0, F_QUERY_METHOD );
+ Set( P_FROG, 0, F_SET_METHOD );
+ SetProp( P_FROG, 0 ); // Damit die Attribute auch stimmen.
+ Set( P_ALCOHOL, 0, F_QUERY_METHOD );
+ Set( P_ALCOHOL, 0, F_SET_METHOD );
+ SetProp(P_ALCOHOL, 0 );
+ Set( P_DRINK, 0, F_QUERY_METHOD );
+ Set( P_DRINK, 0, F_SET_METHOD );
+ SetProp(P_DRINK, 0 );
+ Set( P_FOOD, 0, F_QUERY_METHOD );
+ Set( P_FOOD, 0, F_SET_METHOD );
+ SetProp(P_FOOD, 0 );
+ Set( P_BLIND, 0, F_QUERY_METHOD );
+ Set( P_BLIND, 0, F_SET_METHOD );
+ SetProp(P_BLIND, 0 );
+ Set( P_DEAF, 0, F_QUERY_METHOD );
+ Set( P_DEAF, 0, F_SET_METHOD );
+ SetProp(P_DEAF, 0 );
+ Set( P_MAX_HANDS, 0, F_QUERY_METHOD );
+ Set( P_MAX_HANDS, 0, F_SET_METHOD );
+ SetProp( P_MAX_HANDS, 2 );
+ Set( P_HANDS_USED_BY, 0, F_QUERY_METHOD );
+ Set( P_HANDS_USED_BY, 0, F_SET_METHOD );
+ SetProp( P_HANDS_USED_BY, ({}) );
+ Set( P_PARA, 0 );
+ Set( P_NO_REGENERATION, 0, F_QUERY_METHOD );
+ Set( P_NO_REGENERATION, 0, F_SET_METHOD );
+ SetProp(P_NO_REGENERATION, 0 );
+ Set( P_TMP_MOVE_HOOK, 0, F_QUERY_METHOD );
+ Set( P_TMP_MOVE_HOOK, 0, F_SET_METHOD );
+ SetProp(P_TMP_MOVE_HOOK, 0 );
+ Set( P_LAST_DEATH_TIME , time() );
+ // damit der Teddy o.ae. mitbekommt, dass man jetzt tot ist ]:->
+ SetProp( P_HP, 0 );
+ SetProp( P_SP, 0 );
+}
+
+varargs protected int second_life( object corpse )
+{
+ int lost_exp, level;
+ // Es gibt Funktionen, die sollte man nicht per Hand aufrufen duerfen ;-)
+ if ( extern_call() && previous_object() != ME )
+ return 0;
+
+ if ( query_once_interactive(ME) && IS_LEARNING(ME) ){
+ tell_object( ME, "Sei froh, dass Du unsterblich bist, sonst waere "
+ "es eben zu Ende gewesen.\n" );
+ return 1;
+ }
+
+ if ( !IS_SEER(ME) || (level = QueryProp(P_LEVEL)) < 20 )
+ lost_exp = QueryProp(P_XP) / 3;
+ else
+ lost_exp = QueryProp(P_XP) / (level - 17);
+
+ AddExp(-lost_exp);
+
+
+ // Todesfolgen setzen....
+ //SetProp( P_DEATH_INFO, 1);
+ if ( !IS_LEARNING(ME) && !QueryProp(P_TESTPLAYER) ) {
+
+ mapping attr = QueryProp(P_ATTRIBUTES);
+ mapping mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
+
+ // Attribute auf 75% vom aktuellen Wert senken
+ mods[A_STR] = -attr[A_STR] + (3 * (attr[A_STR] + mods[A_STR]) / 4);
+ mods[A_CON] = -attr[A_CON] + (3 * (attr[A_CON] + mods[A_CON]) / 4);
+ mods[A_DEX] = -attr[A_DEX] + (3 * (attr[A_DEX] + mods[A_DEX]) / 4);
+ mods[A_INT] = -attr[A_INT] + (3 * (attr[A_INT] + mods[A_INT]) / 4);
+
+ SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+
+ int offs = 220; // 220 heart_beats == 7min20
+ // Die 220 HB sind fix, dazu kommen noch 5 HB pro realem
+ // Attributspunkt. Geteilt wird das ganze noch durch 10, weil im HB
+ // nur alle 10 HBs die TF gecheckt werden. Da wird dann alle 10 HB ein
+ // Punkt abgezogen und wenn 0 erreicht ist, wird das Attribut um eins
+ // regeneriert.
+ SetProp( P_DEATH_INFO, ([
+ A_STR: (offs + 5 * (attr[A_STR] + mods[A_STR]))/10; attr[A_STR],
+ A_CON: (offs + 5 * (attr[A_CON] + mods[A_CON]))/10; attr[A_CON],
+ A_DEX: (offs + 5 * (attr[A_DEX] + mods[A_DEX]))/10; attr[A_DEX],
+ A_INT: (offs + 5 * (attr[A_INT] + mods[A_INT]))/10; attr[A_INT]
+ ]) );
+
+ // die suffer_time wird via death_suffering() von
+ // QuerySkillAttribute() abgefragt und geht dann als Malus in
+ // SA_QUALITY mit ein.
+ if ( suffer_time <= 2*SUFFER_TIME )
+ suffer_time += SUFFER_TIME - 1;
+ else
+ suffer_time = 3 * SUFFER_TIME - 1;
+ }
+
+ // Die verschiedenen NotifyPlayerDeath-Funktionen koennen u.U. schlecht
+ // programmiert sein und zuviel Rechenzeit ziehen. Deshalb werden sie mit
+ // einem Limits von 150k bzw. 80k aufgerufen. Ausserdem werden sie nur
+ // gerufen, solange noch min. 25k Ticks da sind.
+ int *limits=query_limits();
+ limits[LIMIT_EVAL] = 150000;
+ limits[LIMIT_COST] = LIMIT_DEFAULT;
+
+ mixed killer = QueryProp(P_KILLER);
+ mixed gi = QueryProp(P_GUILD);
+ if (stringp(gi))
+ gi = find_object("/gilden/"+gi);
+ // jedes Objekt nur einmal, aber nicht via m_indices(mkmapping)) wegen
+ // Verlust der Reihenfolge.
+ object *items = ({killer});
+ if (environment() != killer)
+ items += ({environment()});
+ if (gi != killer && gi != environment())
+ items += ({gi});
+ foreach(object item: items) {
+ if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
+ break;
+ // falls ein NPD() implizit andere Objekt zerstoert hat.
+ if (objectp(item)) {
+ catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
+ ME, killer, lost_exp);publish);
+ }
+ }
+ // jetzt den Rest.
+ limits[LIMIT_EVAL] = 80000;
+ foreach(object item: (environment() ? all_inventory(environment()) : ({}))
+ + deep_inventory(ME)
+ + (objectp(corpse) ? deep_inventory(corpse) : ({}))
+ - items ) {
+ // wenn nicht mehr genug Ticks, haben die restlichen Pech gehabt.
+ if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
+ break;
+ // NPD() koennen andere Objekt zerstoeren.
+ if (objectp(item)) {
+ catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
+ ME, killer, lost_exp);publish);
+ }
+ }
+
+ // Properties zuruecksetzen, sollte nach dem NotifyPlayerDeath()
+ // passieren, weil Objekte sich darin evtl. erstmal noch aufraeumen und
+ // props manipulieren.
+ reset_my_properties();
+ UpdateAttributes(); // Beim Tod werden Dinge entfernt, Attribute pruefen
+
+ // Auch Bewegung erst nach den NPD(), da diese den Spieler bewegen
+ // koennten, was eine Bewegung aus dem Todesraum waere, wenn die Bewegung
+ // vor dem NPD() stattfaende.
+ SetProp( P_GHOST, 1 ); // nach reset_my_properties() !
+ clone_object( "room/death/death_mark" )->move( ME, M_NOCHECK );
+
+ return 1;
+}
+
+
+public int AddHpHook( object ob )
+{
+ object *hooks;
+
+ if ( !objectp(ob) )
+ return 0;
+
+ if ( !pointerp(hooks = Query(P_HP_HOOKS)) ){
+ Set( P_HP_HOOKS, ({ ob }) );
+ return 1;
+ }
+
+ if ( member( hooks, ob ) >= 0 )
+ return 0;
+
+ Set( P_HP_HOOKS, (hooks - ({0})) + ({ ob }) );
+ return 1;
+}
+
+
+public int RemoveHpHook( object ob )
+{
+ object *hooks;
+
+ if ( !pointerp(hooks = Query(P_HP_HOOKS)) )
+ return 0;
+
+ Set( P_HP_HOOKS, hooks - ({ ob, 0 }) );
+ return 1;
+}
+
+
+static int _query_age() {
+ return Set(P_AGE, age, F_VALUE);
+}
+
+static int _set_hp( int hp )
+{
+ object *hooks;
+ int ret, i, old;
+
+ if ( (old = Query(P_HP, F_VALUE)) == hp )
+ return old;
+
+ ret = life::_set_hp(hp);
+
+ if ( ret == old )
+ return ret;
+
+ // Call old hooks in all objects... destructed objects will be ignored.
+ if (pointerp(hooks = Query(P_HP_HOOKS)))
+ call_other(hooks, "NotifyHpChange");
+
+ // Call new hooks.
+ HookFlow(H_HOOK_HP,ret);
+
+ // Report-ausgabe
+ status_report(DO_REPORT_HP, ret);
+
+ return ret;
+}
+
+
+static int _set_sp( int sp )
+{
+ object *hooks;
+ int ret, i, old;
+
+ if ( (old = Query(P_SP,F_VALUE)) == sp )
+ return old;
+
+ ret = life::_set_sp(sp);
+
+ if ( ret == old )
+ return ret;
+
+ // Call old hooks in all objects... destructed objects will be ignored.
+ if (pointerp(hooks = Query(P_HP_HOOKS)))
+ call_other(hooks, "NotifyHpChange");
+
+ // Call new hooks.
+ HookFlow(H_HOOK_SP,ret);
+
+ // Report-ausgabe
+ status_report(DO_REPORT_SP, ret);
+
+ return ret;
+}
+
+static int _set_poison(int n)
+{
+ int old = Query(P_POISON, F_VALUE);
+ if (old == n )
+ return old;
+ n = life::_set_poison(n);
+ if ( n == old )
+ return n;
+ // ggf. Statusreport ausgeben
+ if (interactive(ME))
+ status_report(DO_REPORT_POISON, n);
+ return n;
+}
+
+static int _set_max_poison(int n)
+{
+ if (n >= 0)
+ {
+ Set(P_MAX_POISON, n, F_VALUE);
+ int maxp=QueryProp(P_MAX_POISON); // koennte ne Querymethode drauf sein...
+ if (QueryProp(P_POISON) > maxp)
+ SetProp(P_POISON, maxp);
+ }
+ GMCP_Char( ([P_MAX_POISON: n]) );
+ return n;
+}
+
+static int _set_max_hp( int hp )
+{
+ if (hp >= 0)
+ {
+ Set(P_MAX_HP, hp, F_VALUE);
+ int maxhp=QueryProp(P_MAX_HP); // koennte ne Querymethode drauf sein...
+ if (QueryProp(P_HP) > maxhp)
+ SetProp(P_HP, maxhp);
+ }
+ GMCP_Char( ([P_MAX_HP: hp]) );
+ return hp;
+}
+
+static int _set_max_sp( int sp )
+{
+ if (sp >= 0)
+ {
+ Set(P_MAX_SP, sp, F_VALUE);
+ int maxsp=QueryProp(P_MAX_SP); // koennte ne Querymethode drauf sein...
+ if (QueryProp(P_SP) > maxsp)
+ SetProp(P_SP, maxsp);
+ }
+ GMCP_Char( ([P_MAX_SP: sp]) );
+ return sp;
+}
+
+static int _set_ghost( int g ) {
+ object team;
+
+ if(!g && query_hc_play()>1)
+ {
+ write("HAHAHA, DU BIST AUF EWIG MEIN.\n");
+ return Query(P_GHOST);
+ }
+
+ g = Set( P_GHOST, g );
+
+ if ( g && objectp(team = Query(P_TEAM)) )
+ team->RemoveMember(ME);
+
+ return g;
+}
+
+
+public int undie()
+{
+ mixed x, di;
+ mapping attr, mods;
+
+ if ( !this_interactive() || !previous_object() )
+ return 0;
+
+ if ( !IS_ARCH(this_interactive()) || !IS_ARCH(getuid(previous_object())) ||
+ process_call() )
+ log_file( "UNDIE", sprintf( "%s %O -> %O\n", dtime(time())[5..16],
+ this_interactive(), ME ) );
+
+ if ( x = Query(P_DEADS) )
+ x--;
+
+ Set( P_DEADS, x );
+
+ x = QueryProp(P_XP);
+
+ if ( (di = QueryProp(P_LEVEL)) < 20 || !IS_SEER(ME) )
+ x = (int)(x * 1.5);
+ else
+ // Umweg ueber float, weil bei hohen XP+Level sonst 32Bit nicht
+ // mehr ausreichen -> negative XP
+ x = (int) ( x * ((float) (di - 17) / (di - 18)) );
+
+ Set( P_XP, x );
+
+ attr = QueryProp(P_ATTRIBUTES) || ([]);
+ mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
+
+ if ( mappingp(di = QueryProp(P_DEATH_INFO)) ){
+ // Beim naechsten heart_beat checken
+ // Zesstra: Wieso eigentlich? Die Modifier werden doch direkt hier
+ // geloescht. So expired man auch einen Teil von nicht-undie-ten Toden
+ // vorzeitig...? Mal auskommentiert. 29.10.2007
+ //di[A_STR] = 1;
+ //di[A_DEX] = 1;
+ //di[A_INT] = 1;
+ //di[A_CON] = 1;
+ }
+ else
+ di = ([]);
+
+ mods[A_STR] = ((4 * (attr[A_STR] + mods[A_STR])) / 3) - attr[A_STR];
+ mods[A_DEX] = ((4 * (attr[A_DEX] + mods[A_DEX])) / 3) - attr[A_DEX];
+ mods[A_INT] = ((4 * (attr[A_INT] + mods[A_INT])) / 3) - attr[A_INT];
+ mods[A_CON] = ((4 * (attr[A_CON] + mods[A_CON])) / 3) - attr[A_CON];
+
+ if ( mods[A_STR] >= 0 ) {
+ m_delete( mods, A_STR );
+ m_delete( di, A_STR);
+ }
+ if ( mods[A_DEX] >= 0 ) {
+ m_delete( mods, A_DEX );
+ m_delete( di, A_DEX);
+ }
+ if ( mods[A_INT] >= 0 ) {
+ m_delete( mods, A_INT );
+ m_delete( di, A_INT);
+ }
+ if ( mods[A_CON] >= 0 ) {
+ m_delete( mods, A_CON );
+ m_delete( di, A_CON);
+ }
+
+ SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+ if (sizeof(di))
+ SetProp( P_DEATH_INFO, di );
+ else
+ SetProp( P_DEATH_INFO, 0);
+
+ suffer_time -= ((SUFFER_TIME)-1);
+
+ if ( suffer_time < 0 )
+ suffer_time = 0;
+
+ Set( P_GHOST, 0 );
+ return 1;
+}
+
+
+varargs public void die( int poisondeath, int extern)
+{
+ // laeuft schon ein die()? Fehler ausloesen, Ursache rekursiver die() soll
+ // gefunden werden. DINFO_EVAL_NUMBER wird in jedem Top-Level Call im
+ // driver erhoeht, d.h. gleiche Zahl signalisiert ein rekursives die().
+
+ if (die_in_progress == debug_info(DINFO_EVAL_NUMBER))
+ {
+ // TODO: ist das die_in_progress aus dem letzten Backend-Cycle?
+ raise_error(sprintf(
+ "die() in %O gerufen, aber die() laeuft bereits!\n",
+ this_object()));
+ }
+ die_in_progress = debug_info(DINFO_EVAL_NUMBER);
+
+ // Fuer HC-Player ists jetzt gelaufen...
+ if(query_hc_play()==1)
+ {
+ set_hc_play(capitalize(geteuid(ME)),time());
+ SetDefaultHome("/room/nirvana");
+ SetDefaultPrayRoom("/room/nirvana");
+ SetProp(P_START_HOME,"/room/nirvana");
+ log_file("HCDEAD",dtime(time())+" "+capitalize(geteuid(ME))
+ +" geht in das Nirvana ein!\n");
+ }
+
+ // Wenn das die() direkt von aussen gerufen wurde, muss P_KILLER hier
+ // gespeichert werden.
+ if (extern_call())
+ SetProp(P_KILLER, previous_object());
+
+ // Sichern der zu loeschenden Properties. Diese Props werden im Verlauf des
+ // Todes zurueckgesetzt. Einige Magier wollen diese Daten aber spaeter
+ // noch haben und fragen teilweise P_LAST_DEATH_PROPS im
+ // NotifyPlayerDeath() ab. Daher wird der Kram jetzt hier schonmal
+ // gesichert.
+ // BTW: Props mit Mappings/Arrays sollten kopiert werden.
+ SetProp(P_LAST_DEATH_PROPS, ([
+ P_POISON : QueryProp(P_POISON),
+ P_FROG : QueryProp(P_FROG),
+ P_ALCOHOL : QueryProp(P_ALCOHOL),
+ P_DRINK : QueryProp(P_DRINK),
+ P_FOOD : QueryProp(P_FOOD),
+ P_BLIND : QueryProp(P_BLIND),
+ P_DEAF : QueryProp(P_DEAF),
+ P_MAX_HANDS : QueryProp(P_MAX_HANDS),
+ P_PARA : QueryProp(P_PARA),
+ P_NO_REGENERATION : QueryProp(P_NO_REGENERATION),
+ P_HP : QueryProp(P_HP),
+ P_SP : QueryProp(P_SP),
+ P_LAST_DEATH_TIME : QueryProp(P_LAST_DEATH_TIME )
+ ]) );
+
+ // call the inherited die() with 10 Mio Ticks which will be accounted as 1
+ // Tick... ;-)
+ int *limits = query_limits();
+ limits[LIMIT_EVAL] == 10000000;
+ limits[LIMIT_COST] == LIMIT_UNLIMITED;
+ limited(#'::die, limits, poisondeath, (extern_call() ? 1 : 0));
+
+ // nach dem Tod sollte man auch keine LP mehr haben.
+ SetProp(P_HP, 0);
+
+ // naechster Tod kann kommen. Dekrementierung, da 0 ein gueltiger Wert
+ // fuer DINFO_EVAL_NUMBER waere. abs(), um nicht -__INT_MAX__ zu
+ // dekrementieren.
+ die_in_progress = abs(die_in_progress) - 1;
+}
+
+int defuel_food()
+{
+ int ret;
+ object prev;
+
+ ret=::defuel_food();
+ prev=previous_object();
+ if(!prev || !objectp(prev))
+ {
+ prev=this_object();
+ }
+
+ if(ret<=0)
+ {
+ call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,1);
+ }
+ else
+ {
+ call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,1);
+ }
+ return ret;
+}
+
+int defuel_drink()
+{
+ int ret;
+ object prev;
+
+ ret=::defuel_drink();
+ prev=previous_object();
+ if(!prev || !objectp(prev))
+ {
+ prev=this_object();
+ }
+
+ if(ret<=0)
+ {
+ call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,0);
+ }
+ else
+ {
+ call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,0);
+ }
+ return ret;
+}
+
+public void show_age()
+{ int i,j;
+
+ write("Alter:\t");
+ i = QueryProp(P_AGE);
+ if ((j=i/43200))
+ {
+ write(j + " Tag"+(j==1?" ":"e "));
+ i %= 43200;
+ }
+ if ((j=i/1800))
+ {
+ write(j + " Stunde"+(j==1?" ":"n "));
+ i %= 1800;
+ }
+ if ((j=i/30))
+ {
+ write(j + " Minute"+(j==1?" ":"n "));
+ i %= 30;
+ }
+ write(i*2 + " Sekunden.\n");
+}
+
diff --git a/std/player/moneyhandler.c b/std/player/moneyhandler.c
new file mode 100644
index 0000000..86a17db
--- /dev/null
+++ b/std/player/moneyhandler.c
@@ -0,0 +1,56 @@
+// MorgenGrauen MUDlib
+//
+// player/moneyhandler.c -- money handler for players
+// Nur noch aus Kompatibilitaetsgruenden vorhanden
+//
+// $Id: moneyhandler.c 9051 2015-01-11 20:28:00Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/moneyhandler";
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+#include <container/moneyhandler.h>
+#include <wizlevels.h>
+#include <properties.h>
+#include <money.h>
+#include <moving.h>
+
+public int AddMoney( int amount )
+{
+ object ob;
+
+ if ( !amount )
+ return 1;
+
+ int ret = moneyhandler::AddMoney(amount);
+
+ // ggf. noch loggen
+ if ( ret == MOVE_OK
+ && objectp(ob = find_object("/p/daemon/moneylog"))
+ && amount > 0
+ // dieses muss leider drinbleiben, weil viele nicht-Spieler dieses
+ // erben
+ && query_once_interactive(this_object())
+ && !IS_WIZARD(this_object())
+ && !Query(P_TESTPLAYER) )
+ ob->AddMoney( previous_object(), amount );
+
+ return ret;
+}
+
+public int QueryMoney()
+{
+ object money = present(SEHERKARTEID_AKTIV, this_object());
+ // zusaetzlich zu den anderen Geldquellen auch noch die Seherkarte pruefen.
+ if (money)
+ return moneyhandler::QueryMoney() + money->QueryProp(P_AMOUNT);
+
+ return moneyhandler::QueryMoney();
+}
+
diff --git a/std/player/moving.c b/std/player/moving.c
new file mode 100644
index 0000000..ab62f59
--- /dev/null
+++ b/std/player/moving.c
@@ -0,0 +1,182 @@
+// MorgenGrauen MUDlib
+//
+// player/moving.c -- player moving
+//
+// $Id: moving.c 9434 2016-01-12 12:34:05Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "std/living/moving";
+
+#define NEED_PROTOTYPES
+#include <player/base.h>
+#include <living/description.h>
+#include <player/viewcmd.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <events.h>
+#include <pathd.h>
+
+private nosave string *connections;
+
+public void create()
+{
+ SetProp( P_MSGIN, "kommt an" );
+ SetProp( P_MSGOUT, "geht" );
+ SetProp( P_MMSGIN, "tritt aus einer Schwefelwolke" );
+ SetProp( P_MMSGOUT, "verschwindet mit Knall und Schwefelduft" );
+ Set( P_MSGIN, SAVE, F_MODE );
+ Set( P_MSGOUT, SAVE, F_MODE );
+ Set( P_MMSGIN, SAVE, F_MODE );
+ Set( P_MMSGOUT, SAVE, F_MODE );
+ connections = ({});
+
+ ::create();
+}
+
+
+static mixed _to_remove( object ob )
+{
+ return ob->QueryProp(P_AUTOLOADOBJ) || ob->QueryProp(P_NODROP);
+}
+
+
+// autoload and nodrop object may not fall into the environment
+static varargs int remove( int silent )
+{
+ object* dest_obj = filter( deep_inventory(ME) - ({0}), "_to_remove" );
+ filter_objects( dest_obj, "remove" );
+ filter( dest_obj - ({0}), #'destruct );
+
+ if ( !QueryProp(P_INVIS) && !silent )
+ catch( say( name(WER, 1) + " verlaesst diese Welt.\n", ME );publish );
+
+ if ( ME && !silent )
+ tell_object( ME, "Bis bald!\n" );
+
+ // Im Falle des Resets ist previous_object() == ME, aber
+ // previous_object(0) == 0. Ausserdem ist der Caller-Stack leer. Also
+ // schauen, ob es ein PO gibt, was nicht gleich dem Objekt selber ist, TI
+ // pruefen und
+ if ( this_interactive() != ME
+ && objectp(previous_object()) && previous_object() != ME
+ && object_name(previous_object())[0..7] != "/secure/"
+ && member(object_name(ME), ':') > -1 )
+ log_file( "PLAYERDEST",
+ sprintf( "%s: %O vernichtet von PO %O, TI %O, TP %O\n",
+ dtime(time()), ME, previous_object(),
+ this_interactive(), this_player() ) );
+
+ // Logout-event ausloesen
+ EVENTD->TriggerEvent(EVT_LIB_LOGOUT, ([
+ E_OBJECT: ME,
+ E_PLNAME: getuid(ME),
+ E_ENVIRONMENT: environment() ]) );
+
+ return ::remove();
+}
+
+public string NotifyDestruct(object caller) {
+
+ if (previous_object() != master()
+ || object_name(this_object()) == __FILE__[..<3])
+ return 0;
+
+ // Das Zerstoeren von Spielern wird ggf. geloggt.
+ if ( objectp(caller) && caller != this_object()
+ && getuid(caller) != ROOTID ) {
+ log_file( "PLAYERDEST",
+ sprintf( "%s: %O VERNICHTET von PO %O, TI %O, TP %O\n",
+ dtime(time()), this_object(), caller,
+ this_interactive(), this_player() ) );
+ }
+ // erstmal nix weiter tun, destruct gestatten.
+ return 0;
+}
+
+protected int PreventMove(object dest, object oldenv, int method) {
+ string hcroom;
+ mixed res;
+
+ // gestorbene HC-Spieler duerfen nicht mehr aus dem Nirvana, nicht umgehbar
+ // durch M_NOCHECK
+ if ( interactive(ME) && (query_hc_play()>1) ) {
+ if (objectp(dest))
+ hcroom=object_name(dest);
+ if (sizeof(hcroom)<7 || hcroom[0..5]!="/room/") {
+ return ME_CANT_BE_INSERTED;
+ }
+ }
+
+ // alle anderen Pruefungen sind mit M_NOCHECK egal.
+ if ( (method & M_NOCHECK) )
+ return(::PreventMove(dest,oldenv,method));
+
+ // Spieler duerfen in Raeume mit gesetztem P_NO_PLAYERS nicht hinein
+ if ( dest->QueryProp(P_NO_PLAYERS) && interactive(ME) &&
+ !(method & M_NOCHECK) &&
+ !IS_LEARNER(ME) && (!stringp(res = QueryProp(P_TESTPLAYER))
+ || !IS_LEARNER( lower_case(res) )) ){
+ tell_object( ME, "Da darfst Du als Spieler nicht hin.\n" );
+ return ME_NOT_ALLOWED;
+ }
+
+ return ::PreventMove(dest,oldenv,method);
+}
+
+// Fuck. Ausnahmsweise wegen VC brauch ich nen anderes BLUE_NAME
+#define OLD_BLUE_NAME(ob) (explode(object_name(ob),"#")[0])
+// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
+protected void NotifyMove(object dest, object oldenv, int method) {
+
+ // erstmal ggf. Rauminhalt an Spieler ausgeben.
+ if ( interactive(ME) && !(method & M_NO_SHOW) ) {
+ if (!CannotSee(1))
+ tell_object( ME, "" + env_descr(1) );
+ else if ( QueryProp(P_BRIEF) < 2 )
+ tell_object( ME, "Finsternis.\n" );
+ }
+
+ //dann geerbten Kram machen
+ ::NotifyMove(dest,oldenv,method);
+
+ // Schlussendlich noch fuer den PathD bewegung protokollieren.
+ // (dest nicht geprueft, da ein Spieler nicht ausserhalb jedes Env bewegt
+ // werden kann)
+ if ( interactive() && environment() && query_verb() && objectp(oldenv)) {
+ connections += ({ ({ OLD_BLUE_NAME(oldenv), OLD_BLUE_NAME(dest),
+ query_verb() + " " + (_unparsed_args(2) || ""),
+ method, dest->QueryProp(P_PARA) }) });
+
+ if ( sizeof(connections) > 50
+ && find_call_out("flush_connections") == -1 )
+ call_out( "flush_connections", 0, connections );
+ }
+}
+
+public void flush_connections() {
+ catch(PATHD->add_paths(connections);publish);
+ connections = ({});
+}
+
+/*** Query-Methoden fuer Froesche... ;^) ***/
+static string _query_msgin()
+{
+ return QueryProp(P_FROG) ? "huepft herein" : Query(P_MSGIN);
+}
+
+
+static string _query_msgout()
+{
+ return QueryProp(P_FROG) ? "huepft" : Query(P_MSGOUT);
+}
diff --git a/std/player/objects.c b/std/player/objects.c
new file mode 100644
index 0000000..9263ea3
--- /dev/null
+++ b/std/player/objects.c
@@ -0,0 +1,243 @@
+// MorgenGrauen MUDlib
+//
+// player/objects.c -- object handling for player
+//
+// $Id: objects.c 8675 2014-02-18 20:39:54Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include "/sys/player/filesys.h"
+
+#include <config.h>
+#include <player.h>
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <thing/moving.h>
+
+static int update_object(string str) {
+ object ob;
+ string upd_file;
+ if (!(str=_unparsed_args())) {
+ notify_fail("Usage: Update <object_path>\n"); return 0;
+ }
+ upd_file = find_file(str,".c");
+ if (!upd_file) upd_file=find_file(str);
+ if (!upd_file) {
+ notify_fail(str+": No such file.\n"); return 0;
+ }
+ ob = find_object(upd_file);
+ if (!ob) {
+ notify_fail(upd_file+": No such object.\n"); return 0;
+ }
+ destruct(ob);
+ write(upd_file + ": will be reloaded at next reference.\n");
+ return 1;
+}
+
+/*
+ * "soft" means that the object is given the chance to self-destruct, thus
+ * allowing it to do necessary cleanup like subtracting from the carried
+ * weight of the environment(). We call remove() in the object to be
+ * destructed.
+ */
+static int soft_update_object(string str)
+{
+ object ob;
+ string upd_file;
+ if (!(str=_unparsed_args())) {
+ notify_fail("Usage: update <object_path>\n"); return 0;
+ }
+ upd_file = find_file(str,".c");
+ if (!upd_file) upd_file=find_file(str);
+ if (!upd_file) {
+ notify_fail(str+": No such file.\n"); return 0;
+ }
+ ob = find_object(upd_file);
+ if (!ob) {
+ notify_fail(upd_file+": No such object.\n"); return 0;
+ }
+ if (ob->remove() == 0) {
+ notify_fail(upd_file+": doesn't want to be destructed!\n"); return 0;
+ }
+ write(upd_file + ": will be reloaded at next reference.\n");
+ return 1;
+}
+
+int clone(string str)
+{
+ object ob;
+ string clone_file;
+
+ if (!(str=_unparsed_args())){
+ notify_fail("Usage: clone <object_path>\n"); return 0;
+ }
+ clone_file = find_file(str,".c");
+ if (!clone_file) clone_file=find_file(str);
+ if (!clone_file) {
+ notify_fail(str+": No such file.\n"); return 0;
+ }
+ if (!(ob = clone_object(clone_file)))
+ return notify_fail(str+": Failed to load.\n"), 0;
+
+ /* Some objects destruct themselves rather fast */
+ if (!objectp(ob))
+ return notify_fail(str+": Destructed whilst created.\n"), 0;
+
+ /* try to move the object to my environment */
+ if ((ob->move(this_object(),M_GET)>0) ||
+ (ob->move(environment(),M_NOCHECK)>0))
+ {
+ if (!objectp(ob))
+ return notify_fail(str+": Destructed whilst created.\n"), 0;
+ write("Cloned "+object_name(ob)+".\n");
+ say(this_player()->name(WER,1) + " "
+ + this_player()->QueryProp(P_CLONE_MSG)+".\n");
+ return 1;
+ }
+ say(this_player()->name(WER,1)+" malt wilde Zeichen in die Luft und "
+ +"murmelt vor sich hin, aber nichts passiert...\n");
+ destruct(ob);
+ write(clone_file+": failed to move\n");
+ return 1;
+}
+
+/*
+ * "soft" means that the object is given the chance to self-destruct, thus
+ * allowing it to do necessary cleanup like subtracting from the carried
+ * weight of the environment(). We call remove() in the object to be
+ * destructed.
+ */
+static int soft_destruct_object(string str)
+{
+ object ob;
+ object *obs;
+ string strWER,strWEN;
+
+ if (!(str=_unparsed_args())){
+ notify_fail("Usage: destruct <objectname>\n"); return 0;
+ }
+ strWER = lower_case(str);
+ obs = this_player()->find_obs(strWER,PUT_GET_NONE);
+ if (!obs || !sizeof(obs)) {
+ notify_fail("Kein \"" + str + "\" gefunden.\n");
+ return 0;
+ }
+ ob=obs[0];
+ strWER=ob->name(WER);
+ strWEN=ob->name(WEN);
+ if (!strWER)
+ strWER="jemand";
+ if (!strWEN)
+ strWEN="jemanden";
+
+ if (ob->remove() == 0) {
+ notify_fail(strWER+" will nicht 'destructed' werden!\n");
+ say(this_player()->name(WER,1)+" versucht vergeblich, "+strWEN+
+ " zu atomisieren.\n");
+ return 0;
+ }
+ say(capitalize(strWER)+" "+this_player()->QueryProp(P_DESTRUCT_MSG)+".\n");
+ write(capitalize(strWER)+" wird von dir zerstaeubt.\n");
+ return 1;
+}
+
+static int destruct_object(string str)
+{
+ object ob;
+ object *obs;
+ string strWER,strWEN;
+
+ if (!(str=_unparsed_args())) {
+ notify_fail("Usage: Destruct <objectname>\n"); return 0;
+ }
+ strWER = lower_case(str);
+ obs = this_player()->find_obs(strWER,PUT_GET_NONE);
+ if (!obs || !sizeof(obs)) {
+ notify_fail("Kein \"" + str + "\" gefunden.\n"); return 0;
+ }
+ ob=obs[0];
+ strWER=ob->name(WER);
+ strWEN=ob->name(WEN);
+
+ say(capitalize(strWER)+" "+this_player()->QueryProp(P_DESTRUCT_MSG)+".\n");
+ destruct( ob );
+ write(capitalize(strWER)+" wird von dir zerstaeubt.\n");
+ return 1;
+}
+
+static int load(string str)
+{
+ object env;
+ string file;
+ string err;
+
+ if (!(str=_unparsed_args())) {
+ notify_fail("Usage: load <object_path>\n"); return 0;
+ }
+ file = find_file(str,".c");
+ if (!file) file=find_file(str);
+ if (!file) {
+ notify_fail(str+": No such file.\n"); return 0;
+ }
+ if ( err = catch(load_object(file);publish) )
+ printf("Cannot load %O, err = %O\n",file,err);
+ else write(file+"\n");
+ return 1;
+}
+
+static int exec_playerob(string name)
+{
+ object ob, *inv;
+ int i;
+
+ if (!IS_LORD(this_object())) return 0;
+ if (this_player() != this_interactive()) return 0;
+ if (this_player() != this_object()) return 0;
+ if (!(name=_unparsed_args())) return 0;
+ name="secure/master"->_get_path(name,getuid(this_object()));
+ if (catch(load_object(name);publish) )
+ {
+ write("BUG in "+name+"\n");
+ return 1;
+ }
+ ob=clone_object(name);
+ if (!ob) return 0;
+ if (getuid(ob) != getuid(this_object()))
+ {
+ write("UID conflict.\n");
+ destruct(ob);
+ return 1;
+ }
+ log_file("EXEC", getuid(this_object())+" "+name+" "+dtime(time()));
+ disable_commands();
+ exec(ob,this_object());
+ if (interactive(this_object()) || !interactive(ob))
+ {
+ enable_commands();
+ write("Fehler in exec\n");
+ return 1;
+ }
+ inv=all_inventory(this_object());
+ ob->start_player(capitalize(getuid(this_object())));
+ remove();
+ return 1;
+}
+
+static mixed _query_localcmds()
+{
+ return ({
+ ({"clone","clone",0,WIZARD_LVL}),
+ ({"destruct","soft_destruct_object",0,LEARNER_LVL}),
+ ({"Destruct","destruct_object",0,LEARNER_LVL}),
+ ({"load","load",0,WIZARD_LVL}),
+ ({"update","soft_update_object",0,LEARNER_LVL}),
+ ({"Update","update_object",0,LEARNER_LVL}),
+ ({"exec","exec_playerob",0,LEARNER_LVL})
+ });
+}
diff --git a/std/player/pklog.c b/std/player/pklog.c
new file mode 100644
index 0000000..4613ebe
--- /dev/null
+++ b/std/player/pklog.c
@@ -0,0 +1,108 @@
+// MorgenGrauen MUDlib
+/** \file /std/player/pklog.c
+* Funktion zur Detektion und Loggen von Angriffen von Spielern auf Spieler.
+* \author Zesstra
+* \date 12.08.2008
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+#include <defines.h>
+#include <commands.h>
+#include <wizlevels.h>
+#include <player/base.h>
+
+#define RNAME(x) capitalize(getuid(x))
+
+/** Ist victim in einer Arena oder Schattenwelt?
+ */
+nomask int CheckArenaFight(object victim) {
+ return (object_name(environment(victim))[0..14]=="/d/schattenwelt");
+}
+
+nomask protected int CheckPlayerAttack(object attacker, object victim,
+ string angriffsmsg)
+{
+ // falls mal jemand ne Closure auf diese Funktion in die Finger bekommt und
+ // protected umgeht.
+ if (extern_call())
+ raise_error(sprintf("Extern Call to CheckPlayerAttack in %O!\n",ME));
+
+ // nur Spieler sind interessant.
+ if ( query_once_interactive(attacker) && query_once_interactive(victim)
+ && !( IS_LEARNER(attacker) && IS_LEARNER(victim) ) ) {
+ string filemessage, wizshout;
+ int arena;
+
+ // Arena- oder Schattenweltkampf?
+ arena=CheckArenaFight(attacker);
+
+ wizshout = sprintf("\n**** %s greift %s an. (%s) ***\n",
+ RNAME(attacker), RNAME(victim), object_name(ME));
+ filemessage = sprintf("\n[%s] %s greift %s an. (%s) %s %s\n",
+ strftime("%d%m%y-%H:%M:%S",time()), RNAME(attacker),
+ RNAME(victim), object_name(this_object()),
+ (arena ? "(ARENA)" : ""),
+ (victim->QueryProp(P_TESTPLAYER) ? "(Testspieler)" : ""));
+
+ // Angriffsmsg vom Aufrufer anhaengen.
+ if (stringp(angriffsmsg) && sizeof(angriffsmsg)) {
+ wizshout += angriffsmsg;
+ filemessage += angriffsmsg;
+ }
+ // ggf. echten TI anhaengen oder warnen, falls keiner existiert.
+ if ( this_interactive() != attacker ) {
+ if ( this_interactive() ) {
+ wizshout += "ACHTUNG: TI = " + getuid(this_interactive())
+ +"\n";
+ filemessage += "ACHTUNG: TI = "+getuid(this_interactive())
+ +"\n";
+ }
+ else {
+ filemessage += " ACHTUNG: Kein TI vorhanden!\n";
+ wizshout += " ACHTUNG: Kein TI vorhanden!\n";
+ }
+ }
+ // caller_stack() mitloggen (aber nicht Magier vollscrollen).
+ filemessage += "Callerstack: " + CountUp(map(caller_stack(1),
+ function string (object po) {return to_string(po);}),
+ ", ", ", ") + "\n";
+ // Commandstack anhaengen
+ mixed cstack = command_stack();
+ filemessage += "Commandstack: " + CountUp(map(cstack,
+ function string (mixed arr) {
+ return sprintf("({Original-TP: %O, TP: %O, Kommando: %s})",
+ arr[CMD_ORIGIN],arr[CMD_PLAYER],arr[CMD_TEXT] || "");
+ },", ",", ")) + "\n";
+ // fuer Magier originaeren Befehl anhaengen:
+ if (sizeof(cstack))
+ wizshout += sprintf("Kommando: %s\n",
+ cstack[0][CMD_TEXT] || "<unbekannt>");
+
+ wizshout += "\n";
+
+ // erstmal loggen
+ if ( arena )
+ log_file("ATTACKS_ARENA", filemessage);
+ else
+ log_file("ATTACKS", filemessage);
+
+ // dann Magiern bescheidgeben
+ if ( !arena && !(victim->QueryProp(P_TESTPLAYER)) ) {
+ foreach(object wiz: users()) {
+ if ( IS_LORD(wiz) || IS_DEPUTY(wiz) )
+ tell_object(wiz, wizshout);
+ }
+ }
+ return 1; // Spieler-Spielerkampf
+ }
+ return 0; // kein Spieler-Spielerkampf
+}
+
diff --git a/std/player/potion.c b/std/player/potion.c
new file mode 100644
index 0000000..23d8caa
--- /dev/null
+++ b/std/player/potion.c
@@ -0,0 +1,252 @@
+// MorgenGrauen MUDlib
+//
+// player/potion.c -- potion handling for player
+//
+// $Id: potion.c 9280 2015-08-15 22:20:36Z Arathorn $
+//
+
+#pragma strong_types,save_types
+
+#include <input_to.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <player/potion.h>
+#include <attributes.h>
+#include <living/life.h>
+#include <player/base.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+
+#define POTIONMASTER "/secure/potionmaster"
+
+static mixed *list;
+
+mixed *potionrooms;
+mixed *known_potionrooms;
+
+static mixed _query_potionrooms();
+static mixed _query_known_potionrooms();
+
+protected void create()
+{
+ if (!potionrooms) potionrooms = POTIONMASTER->InitialList();
+ Set(P_POTIONROOMS, NOSETMETHOD, F_SET_METHOD); // no tampering by methods
+ Set(P_POTIONROOMS, SECURED, F_MODE_AS); // no tampering with list
+
+ if (!known_potionrooms) known_potionrooms = ({});
+ Set(P_KNOWN_POTIONROOMS, NOSETMETHOD, F_SET_METHOD);
+ Set(P_KNOWN_POTIONROOMS, SECURED, F_MODE_AS);
+}
+
+static int ReportPotion(string s, int pnum);
+static int SelectWhich(int pnum);
+static int ask_question(int pnum);
+static int get_answer(string erg, int pnum);
+static int raise(string what, int pnum);
+int AddKnownPotion(int nr);
+int RemoveKnownPotion(int nr);
+
+protected void updates_after_restore(int newflag)
+{
+ // P_VISITED_POTIONROOMS ist lang veraltet und unbenutzt, aber bis zum
+ // 21.1.2015 sogar in neuen Spielern gespeichert worden.
+ // Aehnlich fuer P_BONUS_POTIONS. Weg damit.
+ Set(P_VISITED_POTIONROOMS, SAVE|PROTECTED, F_MODE_AD);
+ Set(P_BONUS_POTIONS, SAVE|PROTECTED, F_MODE_AD);
+}
+
+varargs int FindPotion(string s)
+{
+ object po = previous_object();
+ int pnum = POTIONMASTER->HasPotion(po);
+ int flag = 1;
+
+ if ( QueryProp(P_TRANK_FINDEN) && IS_WIZARD(ME) )
+ {
+ return ReportPotion(s, pnum);
+ }
+
+ if ( QueryProp(P_KILLS) )
+ return 0;
+
+ if ( !potionrooms || !sizeof(potionrooms) ||
+ !(POTIONMASTER->InList(po, potionrooms, known_potionrooms)) )
+ flag=0;
+
+ if ( pnum < 0 || !flag )
+ return 0;
+
+ if ( query_input_pending(ME) || query_editing(ME) )
+ {
+ tell_object(ME,
+ "Jetzt haettest Du fast einen Zaubertrank gefunden. Du solltest den\n"
+ "Editor/das More verlassen und es dann noch einmal versuchen!\n");
+ return 1;
+ }
+
+ // Hier der Ausbau der ZTs fuer Geister, wobei das natuerlich in der
+ // Geisterschlossquest immer noch gehen sollte.
+ object env = environment(ME);
+ string fn = old_explode(object_name(env), "#")[0];
+ if ( QueryProp(P_GHOST) && fn[0..24] != "/d/wald/bertram/gschloss/" )
+ {
+ tell_object(ME,"Als Geist einen Zaubertrank? Hier nicht!\n");
+ return 1;
+ }
+ log_file("ARCH/POTIONS", sprintf("%s %s in %s\n", dtime(time()),
+ capitalize(getuid()), object_name(po)));
+
+ return ReportPotion(s, pnum);
+}
+
+static int ReportPotion(string s, int pnum)
+{
+ if (stringp(s) && sizeof(s))
+ tell_object(ME, s);
+ else
+ tell_object(ME, "Du findest einen Zaubertrank, den Du sofort trinkst.\n");
+
+ SelectWhich(pnum);
+
+ return 1;
+}
+
+static int SelectWhich(int pnum)
+{
+ list=({"Intelligenz","Kraft","Geschicklichkeit","Ausdauer"});
+ if (QueryRealAttribute(A_INT)>=20) list-=({"Intelligenz"});
+ if (QueryRealAttribute(A_STR)>=20) list-=({"Kraft"});
+ if (QueryRealAttribute(A_DEX)>=20) list-=({"Geschicklichkeit"});
+ if (QueryRealAttribute(A_CON)>=20) list-=({"Ausdauer"});
+ if (!sizeof(list)) {
+ heal_self(10000000);
+ tell_object(ME, "Der Trank hat Dich geheilt.\n");
+ log_file("ARCH/POTIONS",
+ sprintf(" %s: Heiltrank (noch %d)\n",
+ capitalize(getuid()), sizeof(potionrooms)-1));
+
+ potionrooms -= ({pnum});
+ known_potionrooms -= ({pnum});
+ save_me(1);
+ return 1;
+ }
+ if ( sizeof(list)==1 )
+ return raise(list[0], pnum);
+ ask_question(pnum);
+ return 1;
+}
+
+static int ask_question(int pnum)
+{
+ int i;
+
+ tell_object(ME, "Deine Attribute sehen so aus:\n\n"
+ +sprintf("Intelligenz : %2d (%+d)\n",
+ QueryRealAttribute(A_INT),QueryAttributeOffset(A_INT))
+ +sprintf("Kraft : %2d (%+d)\n",
+ QueryRealAttribute(A_STR),QueryAttributeOffset(A_STR))
+ +sprintf("Geschicklichkeit: %2d (%+d)\n",
+ QueryRealAttribute(A_DEX),QueryAttributeOffset(A_DEX))
+ +sprintf("Ausdauer : %2d (%+d)\n",
+ QueryRealAttribute(A_CON),QueryAttributeOffset(A_CON))
+ );
+
+ tell_object(ME,
+ "\nWas moechtest Du erhoehen? Du hast folgende Moeglichkeiten:\n");
+ for (i=0; i<sizeof(list); i++)
+ tell_object(ME, sprintf("%d) %s\n", i+1, list[i]));
+
+ input_to("get_answer", INPUT_PROMPT,
+ sprintf("\nBitte gib jetzt eine Zahl (1-%d) an: ", i), pnum);
+
+ return 1;
+}
+
+static int get_answer(string erg, int pnum)
+{
+ int num = to_int(erg);
+
+ if ( num > 0 && num <= sizeof(list) )
+ return raise(list[num-1], pnum);
+
+ tell_object(ME, "Deine Wahl war ungueltig. Bitte versuch's nochmal!\n\n");
+ return ask_question(pnum);
+}
+
+static int raise(string what, int pnum) {
+ string attr;
+
+ switch (what)
+ {
+ case "Geschicklichkeit": attr=A_DEX; break;
+ case "Intelligenz": attr=A_INT; break;
+ case "Kraft": attr=A_STR; break;
+ case "Ausdauer": attr=A_CON; break;
+ default: return 0;
+ }
+
+ int yet=QueryRealAttribute(attr);
+ SetRealAttribute(attr, yet+1);
+ tell_object(ME,
+ sprintf("Deine %s hat sich von %d auf %d erhoeht.\n", what, yet, yet+1));
+ log_file("ARCH/POTIONS",
+ sprintf(" %s: %s %d->%d (noch %d)\n",
+ capitalize(getuid()), capitalize(attr),
+ yet, yet+1, sizeof(potionrooms)-1));
+
+ // Wenn die Property gesetzt ist, wird nicht versucht, den gefundenen Trank
+ // aus der ZT-Liste des Magiers auszutragen. Der Nebeneffekt, dass
+ // existierende, angeschlossene ZTs auch nicht mehr aus der ZT-Liste von
+ // testenden Magiern ausgetragen werden, wird dabei in Kauf genommen.
+ if ( !QueryProp(P_TRANK_FINDEN) )
+ {
+ potionrooms -= ({ pnum });
+ known_potionrooms -= ({ pnum });
+ save_me(1);
+ }
+ return 1;
+}
+
+static mixed _query_potionrooms()
+{
+ return copy(Set(P_POTIONROOMS, potionrooms));
+}
+
+static mixed _query_known_potionrooms()
+{
+ return copy(Set(P_KNOWN_POTIONROOMS, known_potionrooms));
+}
+
+int AddKnownPotion(int nr)
+{
+ if (!previous_object() ||
+ object_name(previous_object()) != "/room/orakel")
+ return -1; // Keine Berechtigung
+
+ if (member(known_potionrooms, nr) != -1)
+ return -2; // Nummer bereits eingetragen
+ else
+ {
+ known_potionrooms += ({ nr });
+ return 1;
+ }
+}
+
+int RemoveKnownPotion(int nr)
+{
+ if (!previous_object() ||
+ object_name(previous_object()) != "/room/orakel")
+ return -1; // Keine Berechtigung
+
+ if (member(known_potionrooms, nr) == -1)
+ return -2; // Nummer nicht eingetragen
+ else
+ {
+ known_potionrooms -= ({ nr });
+ return 1;
+ }
+}
diff --git a/std/player/protocols/gmcp.c b/std/player/protocols/gmcp.c
new file mode 100644
index 0000000..b951daa
--- /dev/null
+++ b/std/player/protocols/gmcp.c
@@ -0,0 +1,785 @@
+// MorgenGrauen MUDlib
+//
+// gmcp.c -- Verwaltung von GMCP im Spielerobjekt
+//
+// $Id$
+
+#pragma strong_types,save_types
+#pragma range_check
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+
+#include <regexp.h>
+#include <telnet.h>
+
+#define NEED_PROTOTYPES
+#include <player/base.h>
+#include <thing/properties.h>
+#include <living/attributes.h>
+#include <player/gmcp.h>
+#include <thing/description.h>
+#include <living/description.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <new_skills.h>
+#include <rooms.h>
+#include <tls.h>
+
+inherit "/secure/telnetneg-structs.c";
+
+struct gmcp_mod_s {
+ string id; // Name des GMCP-Moduls (z.B. "MG.Char")
+ int version; // Version des aktivierten moduls
+ closure sendcl; // Handler zum Senden (lfun-closure)
+ closure recvcl; // Handler zum Empfangen (lfunc-closure)
+ mixed data; // optional data of the module
+};
+
+nosave mapping gmcpdata;
+/* Struktur:
+ Jedes Modul hat einen Schluessel im Toplevel, worunter ggf. seine Daten
+ abgelegt sind. Die Daten sind eine struct gmcp_mod_s. Der Schluessel ist
+ der Modulname OHNE Version.
+ */
+#define NEED_PROTOTYPES
+#include "/secure/telnetneg.h"
+#undef NEED_PROTOTYPES
+
+//#define __GMCP_DEBUG__ 1
+// Low priority debug messages
+#define GMCP_DEBUG(pre,msg,prio) if (interactive(this_object()) \
+ && gmcpdata) \
+ GMCP_debug(pre,msg,prio);
+// higher priority error messages
+#define GMCPERROR(msg) if (interactive(this_object()) \
+ && gmcpdata) \
+ GMCP_debug("ERROR",msg,10);
+
+
+// **************** API nach Aussen folgt ab hier ********************
+
+// Wird vom Spielerobjekt gerufen, wenn sich Daten am Charakter veraendert
+// haben, die gesendet werden sollten.
+// Dies ist eigentlich nur ein Wrapper, der die Daten an den Handler eines
+// Moduls weitergibt, welches vom Client aktiviert wurde. Hierzu kommen zur
+// Zeit 2 in Frage: MG.Char (bevorzugt) und Char (minimaler Support).
+/*protected*/ int GMCP_Char(mapping data) {
+
+ if (!mappingp(gmcpdata)) return 0;
+
+ // Als erstes schauen, ob der Client MG.Char aktiviert hat.
+ struct gmcp_mod_s mod = gmcpdata["MG.char"];
+ if (structp(mod) && closurep(mod->sendcl))
+ {
+ funcall(mod->sendcl, data);
+ return 1;
+ }
+ // Dann noch das Modul char pruefen. Das ist aber ziemlich eingeschraenkt
+ // und es gibt hoffentlich nicht viele Clients, die es benutzen.
+ // (Aardwolf-Modul)
+ mod = gmcpdata["char"];
+ if (structp(mod) && closurep(mod->sendcl))
+ {
+ funcall(mod->sendcl, data);
+ return 1;
+ }
+ // Dann noch das Modul Char pruefen. Das ist aber ziemlich eingeschraenkt
+ // und es gibt hoffentlich nicht viele Clients, die es benutzen.
+ // (IRE-Modul)
+ mod = gmcpdata["Char"];
+ if (structp(mod) && closurep(mod->sendcl))
+ {
+ funcall(mod->sendcl, data);
+ return 1;
+ }
+ return 0;
+}
+
+/*protected*/ int GMCP_Channel(string msg, string channel, string sender) {
+ if (!mappingp(gmcpdata)) return 0;
+ // comm.channel Modul aktiv?
+ struct gmcp_mod_s mod = gmcpdata["comm.channel"];
+ if (structp(mod) && closurep(mod->sendcl))
+ {
+ funcall(mod->sendcl, (["chan":channel, "player": sender,
+ "msg": msg]) );
+ return 1;
+ }
+ return 0;
+}
+
+/*protected*/ int GMCP_Room() {
+ if (!mappingp(gmcpdata)) return 0;
+ // MG.room Modul aktiv?
+ struct gmcp_mod_s mod = gmcpdata["MG.room"];
+ if (structp(mod) && closurep(mod->sendcl))
+ {
+ funcall(mod->sendcl, 0);
+ return 1;
+ }
+ return 0;
+}
+
+// **************** Ab hier folgen eher die Lowlevel-Dinge ***********
+private void GMCP_debug(string pre, string msg, int prio) {
+ struct gmcp_mod_s mod = gmcpdata["Core"];
+ if (mod && (mod->data)["Debug"] >= prio)
+ tell_object(this_object(), sprintf("GMCP %s: %s\n",pre,msg));
+}
+
+private void GMCP_send(string cmd, mixed data)
+{
+ GMCP_DEBUG("GMCP_send",sprintf("%s %O",cmd,data), 30);
+ send_telnet_neg_str(sprintf("%c%c%s %s", SB, TELOPT_GMCP,
+ cmd, json_serialize(data)), 1);
+}
+
+private void GMCP_unregister_module(string mod)
+{
+ int version;
+ // Wenn nicht "mod version" Schema, ignorieren
+ if (sscanf(mod, "%s %d", mod, version) != 2)
+ return;
+
+ if (mod=="Core") // darf nicht abgeschaltet werden.
+ return;
+
+ m_delete(gmcpdata, mod);
+}
+
+private void GMCP_register_module(string modname)
+{
+ int version;
+ GMCP_DEBUG("register_module(): trying ... ",modname, 20);
+ // Wenn nicht "mod version" Schema, ignorieren
+ if (sscanf(modname, "%s %d", modname, version) != 2)
+ return;
+
+// GMCP_DEBUG("register_module()",modname + " v" + version);
+
+ // Modul (ggf. mit anderer Version) bereits aktiv?
+ struct gmcp_mod_s mod = gmcpdata[modname];
+ if (structp(mod)) {
+ // wenn gleicher Name und Version, wird nix gemacht, bei anderer Version
+ // wird ein neuer Handler eingetragen und die Daten geloescht.
+ // Wenn nicht-existierende Modul/Version-Kombi angefordert wird, ist das
+ // Modul hinterher aus.
+ if (mod->id == modname && mod->version == version)
+ return;
+ else
+ m_delete(gmcpdata,modname);
+ }
+
+ // Das GMCP-Modul ist nur verfuegbar, wenn es zu der Kombination aus mod und
+ // version einen Handler zum Senden gibt...
+ // Der Handler ist: GMCP_<mod>_v<version>_send, aber in <mod> werden alle
+ // "." durch "_" ersetzt.
+ string replacedname = regreplace(modname, "\\.", "_", RE_GLOBAL);
+ closure sendcl = symbol_function(sprintf("GMCPmod_%s_v%d_send",
+ replacedname, version),
+ this_object());
+ if (!sendcl)
+ return;
+ // Diese Closure darf 0 sein. Dann findet keine Behandlung von vom Client
+ // gesendeten Kommandos statt. Was fuer die meisten Module auch in Ordnung
+ // ist, da sie dem Client keine Kommandos anbieten.
+ closure recvcl = symbol_function(sprintf("GMCPmod_%s_v%d_recv",
+ replacedname, version),
+ this_object());
+
+ GMCP_DEBUG("register_module()",modname+" erfolgreich registriert.",10);
+
+ mod = (<gmcp_mod_s> id: modname, version : version,
+ sendcl : sendcl, recvcl: recvcl, data: ([]) );
+ gmcpdata[modname] = mod;
+
+ // Zum schluss noch den Senden-handler mal rufen, damit der mal alle
+ // verfuegbaren daten sendet.
+ funcall(mod->sendcl, 0);
+}
+
+// Handler fuer das Core Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_Core_v1_recv(string cmd, mixed args)
+{
+ struct gmcp_mod_s mod = gmcpdata["Core"];
+ mapping data = mod->data;
+
+/* if (!mod)
+ {
+ GMCPERROR("Command %s for disabled module ignored.");
+ return;
+ }
+ */
+ GMCP_DEBUG("GMCPmod_Core_v1: ", cmd, 20);
+
+ switch (cmd)
+ {
+ case "Core.Hello":
+ if (mappingp(args))
+ data["Hello"] = (["client": args["client"],
+ "version": args["version"] ]);
+ break;
+ case "Core.Supports.Set":
+ if (pointerp(args))
+ {
+ // Alte Module abschalten/loeschen
+ foreach(string m : data["Supports"])
+ GMCP_unregister_module(m);
+ data["Supports"] = args;
+ // Versuchen, die neuen Module zu registrieren
+ foreach(string m : args)
+ GMCP_register_module(m);
+ }
+ else
+ GMCP_DEBUG("GMCPmod_Core_v1: ",
+ "Data for Core.Supports.Set is no array", 5);
+ break;
+ case "Core.Supports.Add":
+ if (!pointerp(data["Supports"]))
+ data["Supports"] = ({});
+ if (pointerp(args))
+ {
+ foreach(string m: args)
+ GMCP_register_module(m);
+ data["Supports"] += args;
+ }
+ break;
+ case "Core.Supports.Remove":
+ if (!pointerp(data["Supports"]))
+ break;
+ if (pointerp(args))
+ {
+ foreach(string m: args)
+ GMCP_unregister_module(m);
+ data["Supports"] -= args;
+ }
+ break;
+ case "Core.Supports.KeepAlive":
+ break; // this is ignored by us.
+ case "Core.Ping":
+ if (intp(args))
+ data["Ping"] = args;
+ // send a ping back
+ GMCP_send("Core.Ping",0);
+ break;
+ case "Core.Debug":
+ if (intp(args) && args >= 0)
+ data["Debug"] = args;
+ break;
+ default:
+ GMCPERROR(sprintf("Unknown GMCP Core cmd %s with args %O",
+ cmd, args));
+ break;
+ }
+}
+
+// Handler fuer das Core Modul von GMCP
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_Core_v1_send(mapping data)
+{
+ // Zur Zeit nix, spaeter mal Core.Goodbye.
+}
+
+
+// Handler fuer das MG.Char Modul
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_char_v1_send(mapping data)
+{
+ mapping squeue = m_allocate(5,0);
+ struct gmcp_mod_s mod = gmcpdata["MG.char"];
+ // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+ // leider immer alle senden, nicht nur die geaenderten.
+ if (!mappingp(data))
+ {
+ // Alle verfuegbaren Informationen senden...
+ mod->data = m_allocate(6);
+ m_add(mod->data, "MG.char.base",
+ ([P_NAME: Name(WER),
+ P_GUILD: QueryProp(P_GUILD),
+ P_PRESAY: QueryProp(P_PRESAY), // TODO
+ P_TITLE: QueryProp(P_TITLE),
+ "wizlevel": query_wiz_level(this_object()),
+ P_RACE: QueryProp(P_RACE)]) ); // TODO
+ m_add(mod->data,"MG.char.vitals",
+ ([P_HP: QueryProp(P_HP),
+ P_SP: QueryProp(P_SP),
+ P_POISON: QueryProp(P_POISON) ]) );
+ m_add(mod->data,"MG.char.maxvitals",
+ ([P_MAX_HP: QueryProp(P_MAX_HP),
+ P_MAX_SP: QueryProp(P_MAX_SP),
+ P_MAX_POISON: QueryProp(P_MAX_POISON) ]) );
+ m_add(mod->data,"MG.char.attributes",
+ ([ A_STR: QueryAttribute(A_STR),
+ A_INT: QueryAttribute(A_INT),
+ A_DEX: QueryAttribute(A_DEX),
+ A_CON: QueryAttribute(A_CON) ]) );
+ m_add(mod->data,"MG.char.info",
+ ([P_LEVEL: QueryProp(P_LEVEL),
+ P_GUILD_LEVEL: QueryProp(P_GUILD_LEVEL),
+ P_GUILD_TITLE: QueryProp(P_GUILD_TITLE) ]) );
+ m_add(mod->data,"MG.char.wimpy",
+ ([P_WIMPY: QueryProp(P_WIMPY),
+ P_WIMPY_DIRECTION: QueryProp(P_WIMPY_DIRECTION) ]) );
+ m_add(squeue,"MG.char.base");
+ m_add(squeue,"MG.char.vitals");
+ m_add(squeue,"MG.char.maxvitals");
+ m_add(squeue,"MG.char.attributes");
+ m_add(squeue,"MG.char.info");
+ m_add(squeue,"MG.char.wimpy");
+ // dies wird direkt gesendet, weil es nicht gespeichert werden muss. (wird
+ // nur beim Start des Moduls gesendet).
+ GMCP_send("MG.char.infoVars", ([
+ P_LEVEL: "Spielerstufe", P_GUILD_LEVEL: "Gildenstufe",
+ P_GUILD_TITLE: "Gildentitel" ]) );
+ }
+ else
+ {
+ // nur die in data enthaltenen senden.
+ // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+ // muss... *seufz*
+ foreach(string key, mixed val : data)
+ {
+ switch(key)
+ {
+ case P_HP:
+ case P_SP:
+ case P_POISON:
+ (mod->data)["MG.char.vitals"] += ([key: val]);
+ m_add(squeue,"MG.char.vitals");
+ break;
+ case P_MAX_HP:
+ case P_MAX_SP:
+ case P_MAX_POISON:
+ (mod->data)["MG.char.maxvitals"] += ([key: val]);
+ m_add(squeue,"MG.char.maxvitals");
+ break;
+ case P_NAME:
+ (mod->data)["MG.char.base"] += ([key: Name(WER)]);
+ m_add(squeue,"MG.char.base");
+ break;
+ case P_RACE:
+ case P_PRESAY:
+ case P_TITLE:
+ case P_GUILD:
+ (mod->data)["MG.char.base"] += ([key: val]);
+ m_add(squeue,"MG.char.base");
+ break;
+ case A_DEX:
+ case A_STR:
+ case A_CON:
+ case A_INT:
+ (mod->data)["MG.char.attributes"] += ([key: val]);
+ m_add(squeue,"MG.char.attributes");
+ break;
+ case P_LEVEL:
+ case P_GUILD_LEVEL:
+ case P_GUILD_TITLE:
+ (mod->data)["MG.char.info"] += ([key: val]);
+ m_add(squeue,"MG.char.info");
+ break;
+ case P_WIMPY:
+ case P_WIMPY_DIRECTION:
+ (mod->data)["MG.char.wimpy"] += ([key: val]);
+ m_add(squeue,"MG.char.wimpy");
+ break;
+ }
+ }
+ }
+ GMCP_DEBUG("GMCPmod_MG_char_v1_send()",
+ sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+ // Jetzt die squeue senden...
+ foreach(string key : squeue)
+ {
+ GMCP_send(key, (mod->data)[key]);
+ }
+}
+
+// Handler fuer das MG.Char Modul
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_char_v1_recv(string cmd, mixed args)
+{
+ // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+ GMCP_DEBUG("GMCPmod_MG_Char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+/*
+// Handler fuer das MG.Room Modul von GMCP
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_MG_Room_v1_send(mapping data)
+{
+}
+
+// Handler fuer das Room Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_Room_v1_recv(string cmd, mixed args)
+{
+ // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+ GMCP_DEBUG("GMCPmod_MG_Room_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+*/
+
+// Recv Handler fuer das comm.channel Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_comm_channel_v1_recv(string cmd, mixed args)
+{
+ GMCP_DEBUG("GMCPmod_comm_channel_v1_recv",
+ "Client-Kommando ignoriert: "+cmd,20);
+}
+
+// Send Handler fuer das comm.channel Modul von GMCP
+protected void GMCPmod_comm_channel_v1_send(mapping data)
+{
+ // Ganz simpel: einfach raussenden...
+ // Core uebergibt beim Einschalten 0 als data. Dieses modul muss aber beim
+ // Eisnchalten nix machen. Also nur ignorieren.
+ if (mappingp(data))
+ GMCP_send("comm.channel", data);
+}
+
+// Recv Handler fuer das MG.room Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_room_v1_recv(string cmd, mixed args)
+{
+ GMCP_DEBUG("GMCPmod_MG_room_v1_recv",
+ "Client-Kommando ignoriert: "+cmd,20);
+}
+
+// Send Handler fuer das comm.channel Modul von GMCP
+protected void GMCPmod_MG_room_v1_send(mapping data)
+{
+ // Bekommt immer 0 als <data> uebergeben und sucht sich die Daten aus dem
+ // Raum zusammen.
+
+ // Baeh. Warum wird das denn ohne Env gerufen. :-(
+ if (!environment())
+ return;
+
+ // Blind gibt es auch per GMCP nix.
+ if (CannotSee(1))
+ return;
+
+ int restr = environment()->QueryProp(P_MAP_RESTRICTIONS);
+
+ if (restr & MR_NOINFO)
+ return; // gar keine info senden.
+
+ // Anmerkung: int_short() waere cool. Dummerweise uebertraegt das auch
+ // sichtbare Ausgange und Objekte. Insofern: geht nicht.
+ data = ([
+ P_SHORT: process_string(environment()->QueryProp(P_INT_SHORT)||"")+".",
+ "domain": environment()->QueryProp(P_DOMAIN) || "unbekannt",
+ ]);
+
+ // sichtbare Ausgaenge ausgeben
+ mixed hide = environment()->QueryProp(P_HIDE_EXITS);
+ if (hide && !pointerp(hide))
+ data["exits"] = ({}); // alle verstecken
+ else
+ {
+ // Query() verwenden, damit sowohl normale als auch Special Exits
+ // kommen... Die Summe von beiden wuerde auch gehen, aber dann hat man
+ // zwei unnoetige Filter in den Querymethoden. Hngl.
+ mapping exits = environment()->Query(P_EXITS, F_VALUE) || ([]);
+ if (pointerp(hide))
+ data["exits"] = m_indices(exits) - hide;
+ else
+ data["exits"] = m_indices(exits);
+ }
+
+ if (restr & MR_NOUID)
+ data["id"] = "";
+ else
+ data["id"] = hash(TLS_HASH_MD5, object_name(environment()));
+
+ GMCP_send("MG.room.info", data);
+}
+
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_char_v1_send(mapping data)
+{
+ mapping squeue = m_allocate(4,0);
+ struct gmcp_mod_s mod = gmcpdata["char"];
+ // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+ // leider immer alle senden, nicht nur die geaenderten.
+ if (!mappingp(data))
+ {
+ // Alle verfuegbaren Informationen senden...
+ mod->data = m_allocate(4);
+ m_add(mod->data, "char.base", (["name": query_real_name(),
+ "race": QueryProp(P_RACE)]) );
+ m_add(mod->data,"char.vitals", (["hp": QueryProp(P_HP),
+ "mana": QueryProp(P_SP)]) );
+ m_add(mod->data,"char.stats", ([ "str": QueryAttribute(A_STR),
+ "int": QueryAttribute(A_INT),
+ "dex": QueryAttribute(A_DEX),
+ "con": QueryAttribute(A_CON) ]) );
+ m_add(mod->data,"char.status", (["level": QueryProp(P_LEVEL) ]) );
+ m_add(squeue,"char.base");
+ m_add(squeue,"char.vitals");
+ m_add(squeue,"char.stats");
+ m_add(squeue,"char.status");
+ }
+ else
+ {
+ // nur die in data enthaltenen senden.
+ // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+ // muss... *seufz*
+ foreach(string key, mixed val : data)
+ {
+ switch(key)
+ {
+ case P_HP:
+ (mod->data)["char.vitals"] += (["hp": val]);
+ m_add(squeue,"char.vitals");
+ break;
+ case P_SP:
+ (mod->data)["char.vitals"] += (["mana": val]);
+ m_add(squeue,"char.vitals");
+ break;
+ case P_NAME:
+ case P_RACE:
+ (mod->data)["char.base"] += ([key: val]);
+ m_add(squeue,"char.base");
+ break;
+ case A_DEX:
+ case A_STR:
+ case A_CON:
+ case A_INT:
+ (mod->data)["char.stats"] += ([key: val]);
+ m_add(squeue,"char.stats");
+ break;
+ case P_LEVEL:
+ (mod->data)["char.status"] += ([key: val]);
+ m_add(squeue,"char.status");
+ break;
+ }
+ }
+ }
+ GMCP_DEBUG("GMCPmod_char_v1_send()",
+ sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+ // Jetzt die squeue senden...
+ foreach(string key : squeue)
+ {
+ GMCP_send(key, (mod->data)[key]);
+ }
+}
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_char_v1_recv(string cmd, mixed data)
+{
+ // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+ GMCP_DEBUG("GMCPmod_char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+
+// Handler fuer das "Char" Modul von GMCP (Modul von IRE)
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_Char_v1_send(mapping data)
+{
+ mapping squeue = m_allocate(4,0);
+ struct gmcp_mod_s mod = gmcpdata["Char"];
+ // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+ // leider immer alle senden, nicht nur die geaenderten.
+ if (!mappingp(data))
+ {
+ // Alle verfuegbaren Informationen senden...
+ mod->data = m_allocate(4);
+ m_add(mod->data,"Char.Vitals", (["hp": QueryProp(P_HP),
+ "mp": QueryProp(P_SP),
+ "maxhp": QueryProp(P_MAX_HP),
+ "maxmp": QueryProp(P_MAX_SP) ]) );
+ m_add(mod->data,"Char.Status", (["level": QueryProp(P_LEVEL),
+ "guild": QueryProp(P_GUILD) ]) );
+ m_add(squeue,"Char.Vitals");
+ m_add(squeue,"Char.Status");
+ // dies wird direkt gesendet, weil es nicht gespeichert werden muss. (wird
+ // nur beim Start des Moduls gesendet).
+ GMCP_send("Char.StatusVars", ([
+ "level": "Spielerstufe", "guild": "Gilde" ]) );
+ }
+ else
+ {
+ // nur die in data enthaltenen senden.
+ // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+ // muss... *seufz*
+ foreach(string key, mixed val : data)
+ {
+ switch(key)
+ {
+ case P_HP:
+ (mod->data)["Char.Vitals"] += (["hp": val]);
+ m_add(squeue,"Char.Vitals");
+ break;
+ case P_SP:
+ (mod->data)["Char.Vitals"] += (["mp": val]);
+ m_add(squeue,"Char.Vitals");
+ break;
+ case P_MAX_HP:
+ (mod->data)["Char.Vitals"] += (["maxhp": val]);
+ m_add(squeue,"Char.Vitals");
+ break;
+ case P_MAX_SP:
+ (mod->data)["Char.Vitals"] += (["maxmp": val]);
+ m_add(squeue,"Char.Vitals");
+ break;
+ case P_LEVEL:
+ case P_GUILD:
+ (mod->data)["Char.Status"] += ([key: val]);
+ m_add(squeue,"Char.Status");
+ break;
+ }
+ }
+ }
+ GMCP_DEBUG("GMCPmod_Char_v1_send()",
+ sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+ // Jetzt die squeue senden...
+ foreach(string key : squeue)
+ {
+ GMCP_send(key, (mod->data)[key]);
+ }
+}
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_Char_v1_recv(string cmd, mixed args)
+{
+ // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+ GMCP_DEBUG("GMCPmod_Char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+
+// Handler, der von telnetneg.c gerufen wird.
+private void _std_re_handler_gmcp(struct telopt_s opt, int action,
+ int *optargs)
+{
+ switch(action)
+ {
+ case LOCALON:
+ // super!
+ GMCP_DEBUG("recv:", "LOCALON",10);
+ gmcpdata = ([]);
+ opt->data = gmcpdata; // daten auch dort ablegen.
+ // Coremodule in der Version 1 registrieren (es gibt nur eine).
+ GMCP_register_module("Core 1");
+#ifdef __GMCP_DEBUG__
+ GMCPmod_Core_v1_recv("Core.Debug",30);
+#endif
+ break;
+ case LOCALOFF:
+ // alles abschalten und Daten loeschen
+ GMCP_DEBUG("recv:", "LOCALOFF",10);
+ opt->data = 0;
+ gmcpdata = 0;
+ break;
+ case REMOTEON:
+ case REMOTEOFF:
+ // Huch. Auf Clientseite ist GMCP eigentlich nie an. Ignorieren...
+ GMCP_DEBUG("recv:", "Huh? REMOTE state changed?",50);
+ break;
+ case SB:
+ // Der eigentlich interessante Fall... GMCP-Kommandos
+ if (!mappingp(gmcpdata)) return; // GMCP wohl nicht eingeschaltet...
+ string cmd;
+ mixed args;
+ string payload=to_string(optargs);
+ GMCP_DEBUG("recv", payload,10);
+ if (sscanf(payload,"%s %s", cmd, args) != 2) {
+ // ist vermutlich ein Kommando ohne daten (oder Muell)
+ cmd = payload;
+ //args = 0;
+ }
+ else
+ {
+ string err=catch(args = json_parse(args);nolog);
+ if (err)
+ {
+ printf("\nFehler beim Parsen einer GMCP-Nachricht: %s. "
+ "Nachricht war: '%s'\n"
+ "Befehl: '%s', Argument: '%s'\n\n",err,payload,cmd,args||"");
+ return;
+ }
+ }
+ GMCP_DEBUG("recv", sprintf("Command: %s, Data: %O", cmd, args),20);
+
+ string *cmdparts = explode(cmd, ".");
+ struct gmcp_mod_s mod;
+ string modname;
+ // versuch, ein Modul fuer das Kommando zu finden. Anfangen mit der
+ // Annahme, dass bis zum letzten Punkt der Modulname geht und dann
+ // in jedem case einen Punkt kuerzer werdend.
+ switch(sizeof(cmdparts))
+ {
+ case 4:
+ modname = implode(cmdparts[0..2],".");
+ GMCP_DEBUG("trying modname... ", modname, 20 );
+ if (member(gmcpdata, modname)) {
+ mod = gmcpdata[modname];
+ funcall(mod->recvcl, cmd, args);
+ break;
+ }
+ // Fall-through!
+ case 3:
+ modname = implode(cmdparts[0..1],".");
+ GMCP_DEBUG("trying modname... ", modname, 20);
+ if (member(gmcpdata, modname)) {
+ mod = gmcpdata[modname];
+ funcall(mod->recvcl, cmd, args);
+ break;
+ }
+ // Fall-through!
+ case 2:
+ modname = implode(cmdparts[0..0],".");
+ GMCP_DEBUG("trying modname... ", modname, 20);
+ if (member(gmcpdata, modname)) {
+ mod = gmcpdata[modname];
+ funcall(mod->recvcl, cmd, args);
+ break;
+ }
+ // Hier ists jetzt nen Fehler.
+ GMCPERROR(sprintf("Unknown GMCP module for cmd %s",cmd));
+ break;
+ default:
+ // zuviele oder zuwenig . ;-)
+ GMCPERROR(sprintf("Illegal GMCP cmd %s with args %O",
+ cmd, args));
+ break;
+ }
+ // sbdata brauchen wir eigentlich nicht mehr.
+ opt->re_wishes->sbdata = 0;
+ break;
+ } // switch (action)
+}
+
+// wird von base.c nach Konnektierung gerufen.
+// Darf aber erst gerufen werden, wenn das Spielerobjekt fertig initialisiert
+// und eingelesen ist.
+protected void startup_telnet_negs()
+{
+ // evtl. war es ein reconnect, dann steht in gmcp noch alter kram drin. Der
+ // muss weg, koennte ja auch sein, dass der Client (jetzt) kein GMCP
+ // mehr
+ // will.
+ gmcpdata = 0;
+
+ // Hack besonderer Sorte: GMCP soll lokal eingeschaltet sein. Auf
+ // Clientseiten ist es laut Protokoll nicht vorgesehen, daher duerfen
+ // (sollten?) wir kein DO an den Client senden. Wir brauchen aber einen
+ // remote handler, um die Wuensche vom Client zu verarbeiten. Daher erstmal
+ // nur den local handler binden (und gleichzeitig negotiation anstossen) und
+ // dann direkt danach den remote handler auch binden (ohne erneute
+ // negotiation zu starten). Achja und wir nehmen die gleiche Funktion als
+ // Handler fuer remote und lokal.
+ bind_telneg_handler(TELOPT_GMCP, 0, #'_std_re_handler_gmcp, 1);
+ bind_telneg_handler(TELOPT_GMCP, #'_std_re_handler_gmcp,
+ #'_std_re_handler_gmcp, 0);
+}
+
diff --git a/std/player/quests.c b/std/player/quests.c
new file mode 100644
index 0000000..aae566d
--- /dev/null
+++ b/std/player/quests.c
@@ -0,0 +1,244 @@
+// MorgenGrauen MUDlib
+//
+// player/quests.c -- quest handler
+//
+// $Id: quests.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+// Dieses Modul enhaelt die Quest-spezifischen Teile der Playerobjekte.
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player/life.h>
+#include <player/quest.h>
+#include <thing/properties.h>
+#include <player/base.h>
+#include <living/life.h>
+#undef NEED_PROTOTYPES
+
+#include "/secure/questmaster.h"
+#include <wizlevels.h>
+#include <daemon.h>
+#include <language.h>
+#include <mail.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <events.h>
+
+mixed quests;
+
+int QueryQuest(string questname);
+// local properties prototype
+static mixed _query_quests();
+static int _query_questpoints();
+
+protected void create() {
+ Set(P_QUESTS, NOSETMETHOD, F_SET_METHOD);
+ Set(P_QUESTS, quests = ([]), F_VALUE);
+ Set(P_QUESTS, SECURED, F_MODE);
+ Set(P_QP, SAVE, F_MODE);
+ Set(P_QP, SECURED, F_MODE);
+}
+
+varargs int GiveQuest(string questname, string message) {
+ mixed *quest = QM->QueryQuest(questname);
+
+ // Questname ungueltig
+ if (!quest||!pointerp(quest)||quest==({}))
+ return GQ_KEY_INVALID;
+ // Unbefugter Zugriff auf deaktivierte Quest
+ if (!quest[6]&&!IS_ARCH(this_interactive()))
+ return GQ_IS_INACTIVE;
+ // Unbefugter Zugriff
+ if (member(quest[2], load_name(previous_object()))==-1 &&
+ !IS_ARCH(this_interactive()))
+ return GQ_ILLEGAL_OBJ;
+
+ // Gilde wird in jedem Fall informiert.
+ string guild=GUILD_DIR+QueryProp(P_GUILD);
+ if (find_object(guild) || file_size(guild+".c")>-1)
+ catch( call_other(guild, "NotifyGiveQuest", ME, questname);publish );
+
+ // Quest bereits gesetzt
+ if (QueryQuest(questname))
+ return GQ_ALREADY_SET;
+ AddExp(quest[1]);
+ quests += ([ questname : quest[0]; time() ]);
+ force_save();
+ // Event ausloesen
+ EVENTD->TriggerEvent(EVT_LIB_QUEST_SOLVED,([
+ E_OBJECT: ME,
+ E_PLNAME: getuid(ME),
+ E_ENVIRONMENT: environment(),
+ E_QUESTNAME: questname,
+ E_QP_GRANTED: quest[0] ]) );
+
+ if (message && message!="") {
+ if (message!="__silent__") {
+ message=implode(explode(message,"@@name@@"),
+ capitalize(query_real_name()));
+ }
+ else {
+ message="";
+ }
+ }
+ else
+ message=capitalize(query_real_name())
+ +" hat gerade ein Abenteuer bestanden: "+ questname+"\n";
+ if(message!="")
+ catch(QM->Channel(message);publish);
+ catch(QM->SendMail(questname, quest, ME);publish);
+ return OK;
+}
+
+int DeleteQuest(string questname) {
+ // Quest ist nicht gesetzt
+ if(!QueryQuest(questname))
+ return DQ_NOT_SET;
+
+ mixed *quest = QM->QueryQuest(questname);
+ // Questname ungueltig
+ if (!quest||!pointerp(quest)||quest==({}))
+ return DQ_KEY_INVALID;
+ // Unbefugter Zugriff
+ if (!IS_ARCH(this_interactive()))
+ return DQ_ILLEGAL_OBJ;
+ AddExp(-quest[1]);
+ m_delete(quests, questname);
+ force_save();
+ return OK;
+}
+
+int QueryQuest(string questname) {
+ int dummy;
+
+ // Gaeste haben keine Quests.
+ if( sscanf( getuid(), "gast%d", dummy ) == 1 )
+ return QQ_GUEST;
+ // Uebergebener Parameter "questname" ungueltig oder leer?
+ if(!questname || !stringp(questname) || questname == "")
+ return QQ_KEY_INVALID;
+ // Questname ist tatsaechlich in der Questliste enthalten? Alles klar!
+ if ( member(quests, questname) )
+ return OK;
+ // Ansonsten war der Name wohl ungueltig.
+ return QQ_KEY_INVALID;
+}
+
+int ModifyQuestTime(string qname, int when) {
+ if ( process_call() )
+ return -1;
+
+ // Nur EM+ oder der Tagebuchmaster duerfen die Werte aendern.
+ if ( !IS_ARCH(this_interactive()) &&
+ load_name(previous_object())!="/d/wald/leusel/quest/objs/tagebuch-master")
+ return -1;
+
+ // Questliste ist unerwartet kein Mapping.
+ if ( !mappingp(quests) )
+ return -2;
+
+ // Kein Questname angegeben, oder Spieler hat diese Quest ueberhaupt nicht
+ // geloest.
+ if ( !stringp(qname) || !member(quests, qname) )
+ return -3;
+
+ // Der Tagebuchmaster setzt Eintraege ggf. auf 0, wenn er keine Daten
+ // findet, und EM+ wollen Eintraege auf -1 setzen koennen, um das Einlesen
+ // der Daten noch einmal zu ermoeglichen, d.h. diese Werte sind zusaetzlich
+ // zu gueltigen Zeitwerten erlaubt.
+ if ( !intp(when) || when < -1 || when > time() )
+ return -4;
+
+ // Neuen Wert eintragen.
+ quests[qname,1] = when;
+ return 1;
+}
+
+int QueryQuestTime(string qname) {
+ return quests[qname,1];
+}
+
+// Konvertiert Datenstruktur von quests in ein Mapping mit folgendem Aufbau:
+// quests = ([ "questname" : Abenteuerpunkte; Zeit_des_Questabschlusses, ])
+protected void updates_after_restore(mixed newflag) {
+ // Ganz frischer Spieler? Dann keine Behandlung erforderlich.
+ if ( newflag )
+ return;
+ // Wenn die Questliste noch kein Mapping ist, Konvertierung anstossen.
+ if ( !mappingp(quests) ) {
+ // Wenn noch keine Quests eingetragen sind, Leermapping eintragen.
+ if ( !sizeof(quests) ) {
+ quests = ([:2]);
+ return;
+ }
+ // Vorsichtshalber Leereintraege rausziehen.
+ quests -= ({({})});
+ // Liste der Questzeiten aus dem Spieler auslesen. Wenn nicht vorhanden,
+ // Array mit -1-Elementen passender Laenge erzeugen.
+ // -1 an allen Stellen eintragen, wo bisher keine Daten vorliegen.
+ // Diese werden dann vom Questtagebuch durch die Daten ersetzt.
+ int *qtimes = QueryProp("questtime")||allocate(sizeof(quests), -1);
+ // Falls die Questliste laenger ist als die Zeitenliste, wird letztere
+ // um die fehlende Laenge in Form von -1-eintraegen ergaenzt, unter der
+ // Annahme, dass die fehlenden Eintraege bisher lediglich nicht
+ // eingelesen wurden. Im umgekehrten Fall werden alle Zeiten verworfen,
+ // da dieser Fall eintritt, wenn einem Spieler eine Quest ausgetragen
+ // wurde und die Listen in der Vergangenheit nicht synchron gehalten
+ // wurden.
+ if ( sizeof(qtimes) < sizeof(quests) ) {
+ qtimes += allocate(sizeof(quests)-sizeof(qtimes), -1);
+ }
+ if ( sizeof(qtimes) > sizeof(quests) ) {
+ qtimes = allocate(sizeof(quests), -1);
+ }
+ // Questdaten und Questzeiten zusammenpferchen. Ergibt folg. Mapping:
+ // temp = ([ ({Questname1, QP1}) : Questzeit, ... ])
+ mapping temp = mkmapping(quests, qtimes);
+ quests = m_allocate(sizeof(quests),2);
+ foreach(mixed qdata, int qtime : temp) {
+ quests += ([ qdata[0] : qdata[1]; qtime ]);
+ }
+ if (QueryProp("questtime")) {
+ SetProp("questtime",0);
+ Set("questtime", SAVE, F_MODE_AD);
+ }
+ }
+}
+
+// **** local property methods
+static int _query_questpoints() {
+ int qp;
+
+ if ( !mappingp(quests) || !sizeof(quests) ) {
+ return 0;
+ }
+
+ closure qqp = symbol_function("QueryQuestPoints", QM);
+ // Die aktuell gueltigen Abenteuerpunkte aus dem Questmaster holen und
+ // die Questliste damit aktualisieren. qp wird als Referenz mit uebergeben
+ // damit das Additionsergebnis auch nach dem Durchlauf ausserhalb der
+ // Closure verfuegbar ist.
+ // Falls Abenteuerpunkte < 0 existieren, wird die entsprechende Quest
+ // aus der Liste ausgetragen.
+ walk_mapping(quests,
+ function void (string qname, int qpoints, int qtime, int sum)
+ {
+ qpoints = funcall(qqp, qname);
+ if (qpoints<0)
+ m_delete(quests,qname);
+ else
+ sum += qpoints;
+ }, &qp);
+
+ Set(P_QP, qp);
+ return qp;
+}
+
+static mixed _query_quests() {
+ return copy(quests);
+}
diff --git a/std/player/reputation.c b/std/player/reputation.c
new file mode 100644
index 0000000..9fb3d17
--- /dev/null
+++ b/std/player/reputation.c
@@ -0,0 +1,73 @@
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <reputation.h>
+
+private mapping reputations = ([ ]);
+
+/*
+ * Argumente:
+ * repid = Reputation-ID im Repmaster
+ * value = Wert um den die Reputation geaendert werden soll,
+ * positiv oder negativ
+ * silent = Keine Std-Meldung ausgeben
+ *
+ * Return:
+ * REP_RET_WRONGARGS = Falsche Argumente
+ * REP_RET_INACTIVE = Rep inaktiv (kann derzeit nicht geaendert werden)
+ * REP_RET_INVALIDUID = Unzulaessie UID
+ * REP_RET_ALREADYMAX = Rep bereits maximum / minimum
+ * REP_RET_INVALIDREP = Reputation nicht vorhanden
+ *
+ * REP_RET_SUCCESS = Reputation wurde veraendert
+ * REP_RET_SUCCESSCUT = Reputation wurde auf Min / Max veraendert
+ */
+public varargs int ChangeReputation(string repid, int value,
+ int silent) {
+ string uid, changemsg; int newval; mapping rep;
+
+ if(!intp(value) || !value || !stringp(repid) || !sizeof(repid))
+ return REP_RET_WRONGARGS;
+ if(!mappingp(rep = REPMASTER->GetReputationData(repid)))
+ return REP_RET_INVALIDREP;
+ if(!(rep["flags"] & REP_FLAG_ACTIVE))
+ return REP_RET_INACTIVE;
+ if(REPMASTER->CheckValidUid(repid, previous_object()) < 1)
+ return REP_RET_INVALIDUID;
+ if(reputations[repid] >= REP_MAXIMUM || reputations[repid] <= REP_MINIMUM)
+ return REP_RET_ALREADYMAX;
+
+ if(reputations[repid] + value > REP_MAXIMUM)
+ newval = reputations[repid] + value - REP_MAXIMUM;
+ else if(reputations[repid] - value < REP_MINIMUM)
+ newval = reputations[repid] + value + REP_MINIMUM;
+
+ if(!silent &&
+ stringp(changemsg = REPMASTER->GetDefaultChangeMsg(repid,
+ newval || value)))
+ tell_object(this_object(), changemsg);
+
+ reputations[repid] += newval || value;
+
+ return newval ? REP_RET_SUCCESSCUT : REP_RET_SUCCESS;
+}
+
+/*
+ * Argumente:
+ * repid = Reputation-ID im Repmaster
+ *
+ * Return:
+ * 0 = Reputation noch nicht veraendert / enthalten
+ * !0 = Reputationswert
+ */
+public int GetReputation(string repid) { return reputations[repid]; }
+
+/*
+ * Return:
+ * Mappingkopie aller gespeicherten Reputationswert
+ */
+public mapping GetReputations() { return copy(reputations); }
diff --git a/std/player/restrictions.c b/std/player/restrictions.c
new file mode 100644
index 0000000..d71e14c
--- /dev/null
+++ b/std/player/restrictions.c
@@ -0,0 +1,130 @@
+// MorgenGrauen MUDlib
+//
+// player/restrictions.c -- container aspect of players
+//
+// $Id: restrictions.c 9020 2015-01-10 21:49:41Z Zesstra $
+
+// This is a simple container to put objects in. It defines all functions
+// which are necessary to describe an object which can be filled with
+// other things.
+//
+// It will support restrictions for volume, weight etc.
+//
+// The following properties are defined:
+// P_MAX_WEIGHT - maximum weight which container can carry
+// P_WEIGHT_CONTENTS - current contents
+// P_WEIGHT - builtin property: read->total weight, write->own weight
+//
+// Functions for manipulation of weight
+// MayAddWeight(weight) - Can <weight> be inserted?
+// AddWeight(weight) - Add an amount of <weight>
+//
+// IMPORTANT: unit should be interpreted as grams (g).
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/restrictions";
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <hook.h>
+#include <living/skills.h>
+#include <attributes.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <wizlevels.h>
+#include <container.h>
+#include <defines.h>
+#include <new_skills.h>
+
+//Liste von Objekten, in denen InsertNotify() gerufen wird, wenn etwas in den
+//Spieler bewegt wurde.
+nosave object *InsertHooks=({});
+
+// local properties prototypes
+static int _query_max_weight();
+static mixed _set_frog(mixed arg);
+
+void create()
+{
+ ::create();
+
+ Set(P_MAX_WEIGHT, NOSETMETHOD, F_SET_METHOD);
+ Set(P_MAX_WEIGHT, SECURED, F_MODE);
+ offerHook(H_HOOK_INSERT, 1);
+}
+
+// **** local property methods
+static int _query_max_weight() {
+ int str,val;
+ mixed ski;
+
+ if (QueryProp(P_GHOST) && !IS_WIZARD(ME))
+ return 0;
+ str=QueryAttribute(A_STR);
+ ski = UseSkill(SK_CARRY, ([SI_SKILLARG : str ]));
+
+ if (!intp(ski))
+ ski = 0;
+
+ if (str<0) {
+ val=9200+str*160+(int)ski;
+ if (val<3000) val=3000;
+ return val;
+ }
+ val = 9200+str*800+(int)ski;
+ if (val<3000)
+ val = 3000;
+ return val;
+}
+
+static mixed _set_frog(mixed arg) {
+ mixed res;
+
+ res=Set(P_FROG,arg);
+ if (res)
+ SetProp(P_ATTRIBUTES_MODIFIER,({"#frosch",([A_STR:-30])}));
+ else
+ SetProp(P_ATTRIBUTES_MODIFIER,({"#frosch",0 }));
+ return res;
+}
+
+public void NotifyInsert(object ob, object oldenv)
+{
+ ::NotifyInsert(ob, oldenv);
+ // Alle Listener im neuen Hooksystem vom InsertHook informieren
+ HookFlow(H_HOOK_INSERT, ob);
+ // Alle Listener im alten InsertHook informieren
+ if (sizeof(InsertHooks))
+ {
+ foreach(object h: &InsertHooks)
+ {
+ if (h && environment(h) == ME)
+ h->InsertNotify(ob);
+ else
+ h=0;
+ }
+ InsertHooks-=({0}); // genullte Elemente loeschen
+ }
+}
+
+void AddInsertHook(object ob)
+{
+ if (member(InsertHooks,ob)!=-1 || environment(ob)!=this_object())
+ return;
+ InsertHooks+=({ob});
+}
+
+void RemoveInsertHook(object ob)
+{
+ InsertHooks-=({ob});
+}
+
+object *QueryInsertHooks()
+{
+ return InsertHooks;
+}
+
diff --git a/std/player/shadows/ark_hunger_shadow.c b/std/player/shadows/ark_hunger_shadow.c
new file mode 100644
index 0000000..42d6dff
--- /dev/null
+++ b/std/player/shadows/ark_hunger_shadow.c
@@ -0,0 +1,54 @@
+/* Der Shadow fuer den Hungerfluch */
+/* /d/ebene/ark/wolf/obj/hunger.c */
+/* Shadow: /std/player/shadows/ark_hunger_shadow.c */
+/* Original: /d/ebene/ark/wolf/obj/hunger_obj.c */
+
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <properties.h>
+
+#define BS break_string
+
+object spieler;
+
+void Setzen(object sp)
+{
+ if (!objectp(sp) || !interactive(sp)) return destruct(this_object());
+ spieler=sp;
+ if (!shadow(sp,1)) destruct(this_object());
+}
+
+int _query_food() { return 0; }
+
+int _query_max_food() { return 0; }
+
+int eat_food(int strength, int testonly)
+{
+ if (strength==0) return 1;
+ if (strength>0)
+ {
+ strength=0;
+ if (spieler)
+ {
+ tell_object(spieler, BS("Du hast zwar einen tierischen Hunger, doch irgendwas sagt Dir, dass Du das, was "+
+ "Du gerade essen willst, nicht mehr in Deinen Magen bekommst. Das ist ziemlich "+
+ "gefaehrlich, hoffentlich verhungerst Du nicht !", 78));
+ return 0;
+ }
+ }
+ else
+ {
+ if (spieler)
+ {
+ tell_object(spieler, BS("Dein Hunger vertieft sich so nur, Du leidest Hoellenqualen.", 78));
+ spieler->reduce_hit_points(strength);
+ }
+ return 0;
+ }
+}
+
+void Loeschen() {
+ unshadow();
+ destruct(this_object());
+}
diff --git a/std/player/shadows/block_shadow.c b/std/player/shadows/block_shadow.c
new file mode 100644
index 0000000..f7a4333
--- /dev/null
+++ b/std/player/shadows/block_shadow.c
@@ -0,0 +1,47 @@
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+
+private nosave object pl;
+
+void create()
+{
+ if( IS_BLUE(ME) ) return;
+ shadow( PL, 1);
+ pl = PL;
+}
+
+int
+AddExp(int ep)
+{
+ object block;
+ int diff, lim;
+
+ block = present("\n block", pl);
+ lim = 30 + random(10);
+
+ if ( ep > lim && // Mindestwert
+ previous_object() &&
+ ( previous_object() == pl || // zB. GiveQuest()
+ ( living(previous_object()) && // Oder NPCs
+ !query_once_interactive(previous_object())
+ )
+ )
+ )
+ {
+ diff = block->Gutschreiben(ep-lim);
+ return pl->AddExp(lim+diff);
+ }
+ return pl->AddExp(ep);
+}
+
+void
+SeherHatGenug()
+{
+ unshadow();
+ destruct(this_object());
+}
+
diff --git a/std/player/shadows/morph_shadow.c b/std/player/shadows/morph_shadow.c
new file mode 100644
index 0000000..afc97c4
--- /dev/null
+++ b/std/player/shadows/morph_shadow.c
@@ -0,0 +1,598 @@
+/* -*- lpc -*- */
+//--------------------------------------------------------------------------
+//
+// morph.c
+//
+// (c) Troy (troy@mg.mud.de)
+// Kopieren, Veraendern oder Weitergabe: na klar, immer zu, je schlimmer
+// um so besser
+//
+// Objekt erstellt: 14.08.01, Troy
+//
+// Dieser shadow implementiert generische Verwandlungen. Im Gegensatz oder
+// in Ergänzung zum Tarnhelm sind diese nicht auf die Beschreibung
+// beschränkt, sondern schlagen sich auch in anderen Properties nieder.
+//
+//--------------------------------------------------------------------------
+
+#include <moving.h>
+#include <properties.h>
+#include <wizlevels.h>
+
+//--------------------------------------------------------------------------
+
+#pragma strong_types,save_types
+
+//--------------------------------------------------------------------------
+
+varargs int remove( int silent );
+
+//--------------------------------------------------------------------------
+//
+// Property-Einstellungen je nach Rasse
+//
+//--------------------------------------------------------------------------
+
+private mapping morph_properties;
+
+private object pl; // der schattierte Spieler
+
+//--------------------------------------------------------------------------
+//
+// start_shadow( Spieler, Properties )
+//
+// Startet das Shadowing von Spieler. Properties ist ein Mapping mit
+// den zu ändernden Properties. Dort nicht vorhandene Properties werden
+// zum Spieler durchgereicht. Es werden dort entweder einzelne Werte
+// erwartet (Beispiel: ([ P_GENDER: MALE ])), die dann für alle Rassen
+// gelten, oder closures, die dann ausgeführt werden unter Übergabe der
+// Spielerrasse als Parameter oder aber Mappings mit den Rassennamen
+// (Beispiel: ([ P_GENDER: ([ "Mensch": NEUTER, "Elf": FEMALE,
+// "Zwerg": MALE, ... ]) ])). Ist eine Rasse in dem Rassenmapping nicht
+// vorhanden, so wird das Property zum Spieler durchgereicht. Speziell
+// behandelt werden P_IDS (siehe _query_ids()) und P_NAME (siehe
+// _query_name()).
+//
+//--------------------------------------------------------------------------
+int start_shadow( object _pl, mapping preset )
+{
+ if ( !clonep( this_object() ) )
+ return 1;
+ if ( !_pl || !query_once_interactive( _pl ) )
+ return remove();
+ pl = _pl;
+ morph_properties = deep_copy( preset );
+ shadow( pl, 1 );
+ return 1;
+}
+
+//--------------------------------------------------------------------------
+//
+// stop_shadow()
+//
+// Beendet das Shadowing und zerstört dieses Objekt
+//
+//--------------------------------------------------------------------------
+int stop_shadow( /* void */ )
+{
+ if ( !clonep( this_object() ) )
+ return 0;
+ unshadow();
+ return remove();
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_property( Property )
+//
+// Generische Property-Maskierung. liefert aus morph_properties den zur
+// Rasse des Trägers passenden Eintrag.
+//
+//--------------------------------------------------------------------------
+nomask static mixed _query_property( string prop )
+{
+ string race;
+
+ // Rasse holen.
+ if ( IS_LEARNER(pl) )
+ race = "Magier";
+ else // _query_race() ist notwendig, um closures zu umgehen.
+ race = pl->_query_race();
+
+ if ( member( morph_properties, prop ) == 0 )
+ return pl->Query( prop );
+ if ( closurep( morph_properties[ prop ] ) )
+ return funcall( morph_properties[ prop ], race );
+ if ( mappingp( morph_properties[ prop ] ) )
+ {
+ if ( member( morph_properties[ prop ], race ) == 0 )
+ return pl->Query( prop );
+ if ( closurep( morph_properties[ prop ][ race ] ) )
+ return funcall( morph_properties[ prop ][ race ] );
+
+ return morph_properties[ prop ][ race ];
+ }
+ return morph_properties[ prop ];
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_article()
+//
+// Property-Maskierung für P_ARTICLE
+//
+//--------------------------------------------------------------------------
+int _query_article( /* void */ )
+{
+ return (int)_query_property( P_ARTICLE );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_average_size()
+//
+// Property-Maskierung für P_AVERAGE_SIZE
+//
+//--------------------------------------------------------------------------
+int _query_average_size( /* void */ )
+{
+ return (int)_query_property( P_AVERAGE_SIZE );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_average_weight()
+//
+// Property-Maskierung für P_AVERAGE_WEIGHT
+//
+//--------------------------------------------------------------------------
+int _query_average_weight( /* void */ )
+{
+ return (int)_query_property( P_AVERAGE_WEIGHT );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_body()
+//
+// Property-Maskierung für P_BODY
+//
+//--------------------------------------------------------------------------
+int _query_body( /* void */ )
+{
+ return (int)_query_property( P_BODY );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_gender()
+//
+// Property-Maskierung für P_GENDER
+//
+//--------------------------------------------------------------------------
+int _query_gender( /* void */ )
+{
+ return (int)_query_property( P_GENDER );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_hands()
+//
+// Property-Maskierung für P_HANDS
+//
+//--------------------------------------------------------------------------
+mixed _query_hands( /* void */ )
+{
+ return _query_property( P_HANDS );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_ids()
+//
+// Property-Maskierung für P_IDS - Nicht-Standard, da je nach Ursprungs-
+// geschlecht des Spielers zusätzliche ids fällig werden. Ablauf: Es gibt
+// 5 Schritte, bei deren jeweiligem Versagen die ids des Spielers durch-
+// gereicht werden:
+// 1. P_IDS ist im property-mapping vorhanden
+// 2. a) es ist ein string oder ein array: es wird an die ids des Spielers
+// angehängt und zurückgegeben.
+// b) es ist eine closure. Diese wird ausgeführt unter Übergabe der
+// Spieler-ids, der Rasse des Spielers und der Geschlechter
+// (dieses Objekts und des Spielers). Der Ergebniswert der closure
+// wird direkt zurückgegeben.
+// c) es ist ein mapping. Hier nehmen wir nun das übliche Rassennamen-
+// mapping an -> 3.)
+// 3. Für die Rasse des Spielers wird ein Eintrag gesucht
+// a) er ist ein string oder ein array: er wird an die ids des Spielers
+// angehängt und zurückgegeben.
+// b) er ist eine closure. Diese wird ausgeführt unter Übergabe der
+// Spieler-ids, der Rasse des Spielers und der Geschlechter
+// (dieses Objekts und des Spielers). Der Ergebniswert der closure
+// wird direkt zurückgegeben.
+// c) er ist ein mapping. Es wird angenommen, dass je Geschlecht DIESES
+// Objekts ein Eintrag vorhanden ist. -> 4.)
+// 4. Für das Geschlecht dieses Objekts wird ein Eintrag gesucht
+// a) er ist ein string oder ein array: er wird an die ids des Spielers
+// angehängt und zurückgegeben.
+// b) er ist eine closure. Diese wird ausgeführt unter Übergabe der
+// Spieler-ids, der Rasse des Spielers und der Geschlechter
+// (dieses Objekts und des Spielers). Der Ergebniswert der closure
+// wird direkt zurückgegeben.
+// c) er ist ein mapping. Es wird angenommen, dass je Geschlecht DES
+// Spielers ein Eintrag vorhanden ist. -> 5.)
+// 5. Für das Geschlecht des Spielers wird ein Eintrag gesucht
+// a) er ist ein string oder ein array: er wird an die ids des Spielers
+// angehängt und zurückgegeben.
+// b) er ist eine closure. Diese wird ausgeführt unter Übergabe der
+// Spieler-ids, der Rasse des Spielers und der Geschlechter
+// (dieses Objekts und des Spielers). Der Ergebniswert der closure
+// wird direkt zurückgegeben.
+//
+//--------------------------------------------------------------------------
+mixed _query_ids( /* void */ )
+{
+ string race;
+ mixed ids;
+ int gender, sgender;
+
+ // Test 1.
+ ids = pl->Query( P_IDS );
+ if ( member( morph_properties, P_IDS ) == 0 )
+ return ids;
+
+ // Rasse holen.
+ if ( IS_LEARNER(pl) )
+ race = "Magier";
+ else // _query_race() ist notwendig, um closures zu umgehen.
+ race = pl->_query_race();
+
+ // Geschlechter holen
+ gender = _query_gender();
+ sgender = pl->Query( P_GENDER );
+
+ // Test 2.
+ // string? Dann einfach den normalen ids dazu, genauso mit array
+ if ( stringp( morph_properties[ P_IDS ] ) )
+ return ids + ({ morph_properties[ P_IDS ] });
+ if ( pointerp( morph_properties[ P_IDS ] ) )
+ return ids + morph_properties[ P_IDS ];
+ if ( closurep( morph_properties[ P_IDS ] ) )
+ return funcall( morph_properties[ P_IDS ], ids, race, gender, sgender );
+ // falls kein mapping, dann raus
+ if ( !mappingp( morph_properties[ P_IDS ] ) )
+ return ids;
+
+ // Test 3.
+ if ( member( morph_properties[ P_IDS ], race ) == 0 )
+ return ids;
+ if ( stringp( morph_properties[ P_IDS ][ race ] ) )
+ return ids + ({ morph_properties[ P_IDS ][ race ] });
+ if ( pointerp( morph_properties[ P_IDS ][ race ] ) )
+ return ids + morph_properties[ P_IDS ][ race ];
+ if ( closurep( morph_properties[ P_IDS ][ race ] ) )
+ return funcall( morph_properties[ P_IDS ][ race ], ids, race, gender, sgender );
+ // falls kein mapping, dann raus
+ if ( !mappingp( morph_properties[ P_IDS ][ race ] ) )
+ return ids;
+
+ // Test 4.
+ if ( member( morph_properties[ P_IDS ][ race ], gender ) == 0 )
+ return ids;
+ if ( stringp( morph_properties[ P_IDS ][ race ][ gender ] ) )
+ return ids + ({ morph_properties[ P_IDS ][ race ][ gender ] });
+ if ( pointerp( morph_properties[ P_IDS ][ race ][ gender ] ) )
+ return ids + morph_properties[ P_IDS ][ race ][ gender ];
+ if ( closurep( morph_properties[ P_IDS ][ race ][ gender ] ) )
+ return funcall( morph_properties[ P_IDS ][ race ][ gender ],
+ ids, race, gender, sgender );
+ // falls kein mapping, dann raus
+ if ( !mappingp( morph_properties[ P_IDS ][ race ][ gender ] ) )
+ return ids;
+
+ // Test 5.
+ if ( member( morph_properties[ P_IDS ][ race ][ gender ], sgender ) == 0 )
+ return ids;
+ if ( stringp( morph_properties[ P_IDS ][ race ][ gender ][ sgender ] ) )
+ return ids + ({ morph_properties[ P_IDS ][ race ][ gender ][ sgender ] });
+ if ( pointerp( morph_properties[ P_IDS ][ race ][ gender ][ sgender ] ) )
+ return ids + morph_properties[ P_IDS ][ race ][ gender ][ sgender ];
+ if ( closurep( morph_properties[ P_IDS ][ race ][ gender ][ sgender ] ) )
+ return funcall( morph_properties[ P_IDS ][ race ][ gender ][ sgender ],
+ ids, race, gender, sgender );
+
+ return ids;
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_is_morphed()
+//
+// Property-Methode für "is_morphed"
+//
+//--------------------------------------------------------------------------
+int _query_is_morphed( /* void */ )
+{
+ return 1;
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_max_hands()
+//
+// Property-Maskierung für P_MAX_HANDS
+//
+//--------------------------------------------------------------------------
+int _query_max_hands( /* void */ )
+{
+ return (int)_query_property( P_MAX_HANDS );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_mmsgin()
+//
+// Property-Maskierung für P_MMSGIN
+//
+//--------------------------------------------------------------------------
+string _query_mmsgin( /* void */ )
+{
+ return (string)(_query_property( P_MMSGIN ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_mmsgout()
+//
+// Property-Maskierung für P_MMSGOUT
+//
+//--------------------------------------------------------------------------
+string _query_mmsgout( /* void */ )
+{
+ return (string)(_query_property( P_MMSGOUT ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_msgin()
+//
+// Property-Maskierung für P_MSGIN
+//
+//--------------------------------------------------------------------------
+string _query_msgin( /* void */ )
+{
+ return (string)(_query_property( P_MSGIN ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_msgout()
+//
+// Property-Maskierung für P_MSGOUT
+//
+//--------------------------------------------------------------------------
+string _query_msgout( /* void */ )
+{
+ return (string)(_query_property( P_MSGOUT ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_name()
+//
+// Property-Methode für P_NAME. Leider ist die player-shell so grottig,
+// dass überall angenommen wird, QueryProp(P_NAME) liefere einen String :-|
+// Vollständiges Property daher unter _query_name_full().
+//
+//--------------------------------------------------------------------------
+string _query_name( /* void */ )
+{
+ mixed prop;
+ prop = _query_property( P_NAME );
+ if ( stringp( prop ) )
+ return sprintf( prop, pl->Query( P_NAME ) );
+ if ( pointerp( prop ) )
+ return map( prop,
+ lambda( ({ 'el, 's }),
+ ({#'sprintf, 'el, 's}) ),
+ pl->Query( P_NAME ) )[ WER ];
+ return pl->Query( P_NAME );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_name_full()
+//
+// Property-Methode für "name_full".
+//
+//--------------------------------------------------------------------------
+mixed _query_name_full( /* void */ )
+{
+ mixed prop;
+ prop = _query_property( P_NAME );
+ if ( stringp( prop ) )
+ return sprintf( prop, pl->Query( P_NAME ) );
+ if ( pointerp( prop ) )
+ return map( prop,
+ lambda( ({ 'el, 's }),
+ ({#'sprintf, 'el, 's}) ),
+ pl->Query( P_NAME ) );
+ return pl->Query( P_NAME );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_presay()
+//
+// Property-Maskierung für P_PRESAY
+//
+//--------------------------------------------------------------------------
+string _query_presay( /* void */ )
+{
+ return (string)(_query_property( P_PRESAY ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_race()
+//
+// Property-Maskierung für P_RACE
+//
+//--------------------------------------------------------------------------
+string _query_race( /* void */ )
+{
+ return (string)(_query_property( P_RACE ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_racestring()
+//
+// Property-Maskierung für P_RACESTRING
+//
+//--------------------------------------------------------------------------
+string* _query_racestring( /* void */ )
+{
+ return (string*)_query_property( P_RACESTRING );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_size()
+//
+// Property-Maskierung für P_SIZE
+//
+//--------------------------------------------------------------------------
+int _query_size( /* void */ )
+{
+ return (int)_query_property( P_SIZE );
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_title()
+//
+// Property-Maskierung für P_TITLE
+//
+//--------------------------------------------------------------------------
+string _query_title( /* void */ )
+{
+ return (string)(_query_property( P_TITLE ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+// _query_weight()
+//
+// Property-Maskierung für P_WEIGHT
+//
+//--------------------------------------------------------------------------
+int _query_weight( /* void */ )
+{
+ return (int)(_query_property( P_WEIGHT ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+// id( Text, Level)
+//
+// Die Identifizierung spinnt mit P_NAME-Arrays
+//
+//--------------------------------------------------------------------------
+varargs int id( string str, int lvl )
+{
+ string plname;
+
+ if ( pl->QueryProp( P_GHOST ) )
+ if ( str == "geist" )
+ return 1;
+ else if ( ( sscanf( str, "geist von %s", plname ) == 1 ) &&
+ pl->id( plname ) )
+ return 1;
+ else if ( ( sscanf( str, "geist %s", plname ) == 1 ) &&
+ pl->id( plname ) )
+ return 1;
+
+ return pl->id( str, lvl );
+}
+
+//--------------------------------------------------------------------------
+//
+// long()
+//
+// Die Langbeschreibung im Spieler hadert mit dem Geschlecht NEUTER...
+//
+//--------------------------------------------------------------------------
+varargs string long( /* void */ )
+{
+ string slong;
+ slong = pl->long();
+
+ if ( _query_gender() == NEUTER )
+ {
+ string *along;
+ int i;
+
+ // alle Er-s und er-s suchen...
+ along = regexplode( slong, "\\<[Ee]r\\>" );
+ // ... und das r durch ein s ersetzen.
+ for ( i = 1 ; i < sizeof( along ) ; i += 2 )
+ along[ i ][ 1 ] = 's';
+ slong = implode( along, "" );
+ }
+
+ return slong;
+}
+
+//--------------------------------------------------------------------------
+//
+// short()
+//
+// Die Kurzbeschreibung im Spieler hat ein Problem mit P_NAME-Arrays
+//
+//--------------------------------------------------------------------------
+string short( /* void */ )
+{
+ mixed names;
+ string answer;
+ string title;
+
+ if ( pl->QueryProp( P_INVIS ) )
+ if ( interactive( previous_object() ) &&
+ IS_LEARNING( previous_object() ) )
+ return "(" + pl->Query( P_NAME ) + ") \n";
+ else
+ return (string)0;
+
+ names = _query_name_full();
+ if ( stringp( names ) )
+ names = ({ names, names, names, names });
+
+ if ( pl->QueryProp( P_GHOST ) )
+ answer = "Der Geist " + pl->QueryArticle( WESSEN, 0 ) + names[ WESSEN ];
+ else
+ answer = pl->QueryArticle( WER, 0 ) + names[ WER ];
+ if ( ( title = pl->QueryProp( P_TITLE ) ) &&
+ ( title != "" ) )
+ answer += " " + title;
+ if ( !interactive( pl ) )
+ answer += " (netztot)";
+ return capitalize( answer ) + ".\n";
+}
+
+//--------------------------------------------------------------------------
+//
+// remove( Schnauze )
+//
+// aufräumen
+//
+//--------------------------------------------------------------------------
+varargs int remove( int silent )
+{
+ destruct( this_object() );
+ return 1;
+}
+
+//--------------------------------------------------------------------------
+// -- ENDE --
diff --git a/std/player/shadows/pony.c b/std/player/shadows/pony.c
new file mode 100644
index 0000000..8d3de8d
--- /dev/null
+++ b/std/player/shadows/pony.c
@@ -0,0 +1,59 @@
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+
+//
+// Da der Shadow sich nicht im Player befindet, wird ein
+// Zusatzobjekt benoetigt.
+// 7.10.97 - Die Hook sollte in den Verzeichnissen der Benniequest
+// zu finden sein. - Rumata
+
+#define HOOK "d/wald/kassandra/bennie/obj/pony_hook"
+
+static object pl;
+
+void create()
+{
+ if( IS_BLUE(ME) ) return;
+ shadow( PL, 1);
+ pl=PL;
+ //tell_object(this_player(),"Du besteigst das Pony samt Schatten.\n");
+ clone_object( HOOK ); // Steuerung des shadows ueber die hook
+}
+
+string _query_title()
+{
+ if( pl->QueryProp(P_GENDER) == FEMALE )
+ return "die Ponyreiterin";
+ return "der Ponyreiter";
+}
+
+string _query_msgin() { return "reitet herein"; }
+string _query_msgout() { return "reitet"; }
+
+varargs int move(mixed dest, int method, string dir, string textout,string textin)
+{
+ if( !(method & M_NOCHECK) && dest->QueryProp(P_INDOORS) )
+ {
+ write( "Das Pony weigert sich, dorthin zu traben.\n" );
+ return 1;
+ }
+ return pl->move( dest, method, dir, textout, textin );
+}
+
+int _inventory(string str)
+{
+ if( !environment() || set_light(0)>0 )
+ write( "Du sitzt auf einem Pony.\n" );
+ return pl->_inventory(str);
+}
+
+void absteigen() {
+ unshadow();
+ destruct(ME);
+}
+
+int QueryReiter() { return 1; }
diff --git a/std/player/shadows/sizetest.c b/std/player/shadows/sizetest.c
new file mode 100644
index 0000000..1268911
--- /dev/null
+++ b/std/player/shadows/sizetest.c
@@ -0,0 +1,57 @@
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+
+#define SPIELER "nefis"
+#define DEBUG(x) if (find_player("zesstra"))\
+ tell_object(find_player("zesstra"),\
+ "Nefis P_SIZE: "+x+"\n")
+#define LOG(x) log_file("zesstra/P_SIZE.log", x)
+
+private nosave object pl;
+
+void create()
+{
+ if (!clonep() ) return;
+ pl = find_player(SPIELER) || find_netdead(SPIELER);
+ if (!objectp(pl)) {
+ DEBUG("Playerobjekt nicht gefunden.");
+ destruct(ME);
+ return;
+ }
+ shadow( pl, 1);
+ DEBUG(sprintf("%O shadowed by: %O\n", pl, shadow(pl, 0)));
+}
+
+int _set_size(int sz) {
+ LOG(sprintf("[%s] Aenderung von P_SIZE in %O auf %O,\nCallerstack: %O\n\n",
+ strftime("%c",time()), pl, sz, caller_stack()));
+ return pl->Set(P_SIZE, sz, F_VALUE);
+}
+
+public varargs mixed Set( string name, mixed Value, int Type, int extern ) {
+ if (name == P_SIZE) {
+ LOG(sprintf("[%s] Aenderung von P_SIZE in %O auf %O (Typ: %O) [Set()!]"
+ ",\nCallerstack: %O\n\n",
+ strftime("%c",time()), pl, Value, Type, caller_stack()));
+ }
+ return pl->Set(name, Value, Type, extern);
+}
+
+int stop() {
+ unshadow();
+ destruct(ME);
+ return 1;
+}
+
+void reset() {
+ if (!objectp(pl))
+ stop();
+}
+
+int remove() {
+ stop();
+ return 1;
+}
+
diff --git a/std/player/shadows/tarnhelm_shadow.c b/std/player/shadows/tarnhelm_shadow.c
new file mode 100644
index 0000000..63ea744
--- /dev/null
+++ b/std/player/shadows/tarnhelm_shadow.c
@@ -0,0 +1,140 @@
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <properties.h>
+
+#define WEG() destruct(this_object())
+
+object player;
+int gender;
+string desc;
+
+void _tarn_turn_on(object pl,string txt,int gen)
+{
+ if (!objectp(pl)||!interactive(pl)) return WEG();
+ if (!stringp(txt)||txt=="") return WEG();
+ if (gen<0||gen>2) return WEG();
+ player=pl;
+ desc=capitalize(txt);
+ gender=gen;
+ shadow(pl,1);
+}
+
+int special_verb()
+{
+ string verb;
+
+ verb=query_verb();
+ if (!verb||verb=="") return 0;
+ if (verb[0]=='-') return 1;
+ if (verb=="ruf"||verb=="rufe"||verb=="teile"||verb=="teil"||verb=="mruf"||
+ verb=="mrufe"||verb=="erzaehl"||verb=="erzaehle") return 1;
+ return(0); // non-void funktion, Zesstra
+}
+
+int _query_gender()
+{
+ return gender;
+}
+
+string _query_name()
+{
+ if (!special_verb())
+ return capitalize(desc);
+ return player->Query(P_NAME);
+}
+
+string _query_short()
+{
+ return capitalize(player->name());
+}
+
+string _query_long()
+{
+ string str;
+
+ str=player->name();
+ return capitalize(str)+" ist "+str+" ist "+str+".\n";
+}
+
+string* _query_ids()
+{
+ return player->Query("ids")+({lower_case(desc)});
+}
+
+string _query_race()
+{
+ return desc;
+}
+
+void _tarn_turn_off()
+{
+ unshadow();
+ destruct(this_object());
+}
+
+int _query_article()
+{
+ if (!special_verb())
+ return 1;
+ return(0); // non-void funktion, Zesstra
+}
+
+string _query_presay()
+{
+ return "";
+}
+
+string _query_title()
+{
+ return "";
+}
+
+void Defend(int dam,mixed dam_type,mixed spell,object enemy)
+{
+ object o;
+
+ if (!query_once_interactive(previous_object()))
+ player->Defend(dam, dam_type, spell, enemy);
+ else
+ previous_object()->StopHuntFor(player);
+ if ((o=present("\ntarnhelm",player)))
+ o->DoUnwear();
+ if (this_object()) destruct(this_object());
+}
+
+int Kill(object ob)
+{
+ object o;
+
+ if ((o=present("\ntarnhelm",player)))
+ o->DoUnwear();
+ if (this_object()) destruct(this_object());
+ return(0); // non-void funktion, Zesstra
+}
+
+int InsertEnemy(object ob)
+{
+ object o;
+
+ if (!query_once_interactive(ob))
+ player->InsertEnemy(ob);
+ else
+ ob->StopHuntFor(player);
+ if ((o=present("\ntarnhelm",player)))
+ o->DoUnwear();
+ if (this_object()) destruct(this_object());
+ return 0;
+}
+
+string short()
+{
+ if (old_explode(object_name(previous_object()),"#")[0]=="/obj/werliste")
+ return capitalize(geteuid(player)+" verkleidet als "+player->short());
+ return player->short();
+}
+
+string QueryDisguise()
+{
+ return desc;
+}
diff --git a/std/player/shadows/zaubersh.c b/std/player/shadows/zaubersh.c
new file mode 100644
index 0000000..a19cecc
--- /dev/null
+++ b/std/player/shadows/zaubersh.c
@@ -0,0 +1,220 @@
+#pragma strong_types,save_types
+
+#include <properties.h>
+
+object caster;
+string*ids,name;
+int gender,article,plural;
+string lgdesc;
+string min,mout,mmin,mmout;
+mixed hands;
+
+void Initialize(object _caster,
+ string*_ids,
+ string _name,
+ int _gender,
+ int _article,
+ int _plural)
+{ if(!objectp(_caster) ||
+ !interactive(_caster)||
+ !stringp(_name) ||
+ _name=="" ||
+ _gender<0 ||
+ _gender>2)
+ { destruct(this_object());
+ return;
+ }
+ caster=_caster;
+ ids=_ids;
+ name=_name;
+ gender=_gender;
+ article=_article;
+ plural=_plural;
+ lgdesc=0;
+ shadow(caster,1);
+}
+
+void SetLongDesc(string txt)
+{ if(!stringp(txt)||txt=="")
+ return;
+ lgdesc=txt;
+}
+
+string* _query_ids()
+{ return caster->Query(P_IDS)+ids;
+}
+
+// nicht alle Verben mit veraendertem Aussehen
+int special_verb()
+{ string verb;
+ verb=query_verb();
+ if(!stringp(verb)||verb=="")
+ return 0;
+ if(verb[0]=='-')
+ return 1;
+ if(member(({"ruf","rufe","mruf","mrufe",
+ "teil","teile","erzaehl","erzaehle"}),verb)!=-1)
+ return 1;
+ return 0;
+}
+
+string _query_name()
+{ if(!special_verb())
+ return name;
+ return capitalize(caster->Query(P_NAME));
+}
+
+string _query_short()
+{ if(!special_verb())
+ return caster->Name();
+ return caster->Query(P_SHORT);
+}
+
+string _query_long()
+{ if(!special_verb())
+ { string str;
+ if(lgdesc)
+ return lgdesc;
+ str=caster->name();
+ return break_string(capitalize(str)+" ist "+str+" ist "+str+".",78);
+ }
+ return caster->Query(P_LONG);
+}
+
+int _query_gender()
+{ if(!special_verb())
+ return gender;
+ return caster->Query(P_GENDER);
+}
+
+int _query_article()
+{ if(!special_verb())
+ return article;
+ return caster->Query(P_ARTICLE);
+}
+
+int _query_plural()
+{ if(!special_verb())
+ return plural;
+ return caster->Query(P_PLURAL);
+}
+
+string _query_race()
+{ if(!special_verb())
+ return name;
+ return caster->Query(P_RACE);
+}
+
+varargs int remove(int silent) {
+ unshadow();
+ destruct(this_object());
+ return 1;
+}
+
+void stop_shadow()
+{
+ remove();
+}
+
+string _query_presay()
+{ return"";
+}
+
+string _query_title()
+{ return"";
+}
+
+string _set_msgin(string val)
+{ return min=val;
+}
+
+string _query_msgin()
+{ return min;
+}
+
+string _set_msgout(string val)
+{ return mout=val;
+}
+
+string _query_msgout()
+{ return mout;
+}
+
+string _set_mmsgin(string val)
+{ return mmin=val;
+}
+
+string _query_mmsgin()
+{ return mmin;
+}
+
+string _set_mmsgout(string val)
+{ return mmout=val;
+}
+
+string _query_mmsgout()
+{ return mmout;
+}
+
+mixed _set_hands(mixed val)
+{ return hands=val;
+}
+
+mixed _query_hands()
+{ return hands;
+}
+
+varargs int Defend(int dam,mixed dam_type,mixed spell,object enemy)
+{ object ob;
+ if(!enemy || // Silvana 26.1.2002
+ (!query_once_interactive(previous_object())&&
+ !query_once_interactive(enemy)))
+ return caster->Defend(dam,dam_type,spell,enemy);
+ else
+ { enemy->StopHuntFor(caster);
+ caster->StopHuntFor(enemy);
+ }
+ if(objectp(ob=present("zauberer\nshadow",caster)))
+ ob->remove();
+ if(this_object())
+ remove();
+ return 0;
+}
+
+int Kill(object enemy)
+{ object ob;
+ if(!query_once_interactive(enemy))
+ return caster->Kill(enemy);
+ if(objectp(ob=present("zauberer\nshadow",caster)))
+ ob->remove();
+ if(this_object())
+ remove();
+ return 0;
+}
+
+int InsertEnemy(object enemy)
+{ object ob;
+ if(!query_once_interactive(enemy))
+ return caster->InsertEnemy(enemy);
+ else {
+ enemy->StopHuntFor(caster);
+ caster->StopHuntFor(enemy);
+ }
+ if(objectp(ob=present("zauberer\nshadow",caster)))
+ ob->remove();
+ if(this_object())
+ remove();
+ return 0;
+}
+
+string short()
+{ if(load_name(previous_object()) == "/obj/werliste")
+ return capitalize(geteuid(caster))+" verkleidet als "+caster->short();
+ return caster->short();
+}
+
+varargs string long()
+{ if(lgdesc)
+ return lgdesc;
+ return caster->long();
+}
diff --git a/std/player/skills.c b/std/player/skills.c
new file mode 100644
index 0000000..503da97
--- /dev/null
+++ b/std/player/skills.c
@@ -0,0 +1,621 @@
+// MorgenGrauen MUDlib
+//
+// player/skills.c -- Spielerskills
+//
+// $Id: skills.c 8809 2014-05-08 19:52:48Z Zesstra $
+
+//
+// 2003-01-20: Nun Zooks Baustelle
+//
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/skills";
+
+#include <combat.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <break_string.h>
+#include <wizlevels.h>
+
+#define NEED_PROTOTYPES
+#include <player/base.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+// Dieses Mapping speichert die deaktivierten Skills der einzelnen Gilden
+// Diese werden in den Gilden ueber P_GUILD_DEACTIVATED_SKILL gesetzt.
+nosave mapping deactivated_skills = ([]);
+
+// Flag fuer den Kompatibilitaetsmodus des Kampfs (Emulation von
+// 2s-Alarmzeit). Wenn != 0 speichert das Flag gleichzeitig die Zeit des
+// letzten Heartbeats. Auf diese Zeit wird der Startpunkt eines Spellfatigues
+// ggf. zurueckdatiert. (max. eine Sekunde)
+int spell_fatigue_compat_mode;
+
+// Ein create() fuer das Mapping
+
+protected void create()
+{
+ mapping act;
+
+ ::create();
+
+ // Wir holen die Gilden aus dem Gildenmaster
+ foreach(string guild:
+ (string *)call_other(GUILDMASTER,"QueryProp",P_VALID_GUILDS))
+ {
+ if(catch(act=call_other("/gilden/"+guild,"QueryProp",
+ P_GUILD_DEACTIVATE_SKILLS); publish ))
+ log_file("WEAPON_SKILLS", sprintf ("%s: Gilde nicht ladbar: "
+ +"TP: %O, TI: %O, PO: %O, Gilde: %s\n", dtime(time()),
+ this_player(), this_interactive(), previous_object(), guild));
+ else if (act) // wenn act, ins Mapping aufnehmen.
+ deactivated_skills+=([guild:act]);
+ }
+ // keine echte Prop mehr, Save-Modus unnoetig.
+ Set(P_NEXT_SPELL_TIME,SAVE,F_MODE_AD);
+
+ Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
+}
+
+// Das Mapping kann man auch abfragen
+
+public mapping GetDeactivatedSkills()
+{
+ return copy(deactivated_skills);
+}
+
+// Funktion, die sagt ob ein ANY-Skill deaktiviert ist.
+public int is_deactivated_skill(string sname,string guild)
+{
+ if (deactivated_skills[guild])
+ return deactivated_skills[guild][sname];
+ return 0;
+}
+
+
+// Funktion fuer die Waffenskills
+// traegt die allg. Waffenskills ein. Wird ggf. von FixSkills() gerufen.
+// (Das Eintragen bedeutet nicht, dass die aktiv sind! Aber bei Gildenwechsel
+// werden sie nicht eingetragen).
+private void set_weapon_skills() {
+
+ if (QuerySkillAbility(FIGHT(WT_SWORD))<=0)
+ ModifySkill(FIGHT(WT_SWORD),([SI_SKILLABILITY:0]),150,"ANY");
+ if (QuerySkillAbility(FIGHT(WT_AXE))<=0)
+ ModifySkill(FIGHT(WT_AXE),([SI_SKILLABILITY:0]),150,"ANY");
+ if (QuerySkillAbility(FIGHT(WT_SPEAR))<=0)
+ ModifySkill(FIGHT(WT_SPEAR),([SI_SKILLABILITY:0]),150,"ANY");
+ if (QuerySkillAbility(FIGHT(WT_WHIP))<=0)
+ ModifySkill(FIGHT(WT_WHIP),([SI_SKILLABILITY:0]),150,"ANY");
+ if (QuerySkillAbility(FIGHT(WT_KNIFE))<=0)
+ ModifySkill(FIGHT(WT_KNIFE),([SI_SKILLABILITY:0]),150,"ANY");
+ if (QuerySkillAbility(FIGHT(WT_CLUB))<=0)
+ ModifySkill(FIGHT(WT_CLUB),([SI_SKILLABILITY:0]),150,"ANY");
+ if (QuerySkillAbility(FIGHT(WT_STAFF))<=0)
+ ModifySkill(FIGHT(WT_STAFF),([SI_SKILLABILITY:0]),150,"ANY");
+}
+
+// initialisiert die Skills fuer Spieler (momentan: allg. Waffenskills setzen
+// und P_SKILLS_VERSION)
+protected void InitSkills() {
+ mapping ski;
+ // schonmal initialisiert?
+ if (mappingp(ski=Query(P_NEWSKILLS, F_VALUE)) && sizeof(ski))
+ return;
+
+ // allg. Waffenskills aktivieren
+ set_weapon_skills();
+
+ // Version setzen
+ SetProp(P_SKILLSVERSION, CURRENT_SKILL_VERSION);
+ Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
+}
+
+// Updated Skills aus Version 0 und 1 heraus.
+private void FixSkillV1(string skillname, mixed sinfo) {
+ // alte Skills auf mappings normieren
+ if (intp(sinfo)) {
+ sinfo = ([SI_SKILLABILITY: sinfo ]);
+ }
+ // Eine Reihe von Daten werden geloescht, da die Daten aus der
+ // Gilde/Spellbook frisch kommen sollten und sie nicht spieler-individuell
+ // sind: SI_CLOSURE (wird onthefly korrekt neu erzeugt), SI_SKILLARG,
+ // SI_NUMBER_ENEMIES, SI_NUMBER_FRIENDS, SI_DISTANCE, SI_WIDTH, SI_DEPTH,
+ // SI_TESTFLAG, SI_ENEMY, SI_FRIEND.
+ // Ausserdem sind alle SP_* im toplevel falsch, die muessen a) in SI_SPELL
+ // und sollten b) nicht im Spieler gespeichert werden, sondern von
+ // Gilden/Spellbook jeweils frisch kommen.
+ // all dieses Zeug landete in alten Spielern im Savefile.
+ if (mappingp(sinfo))
+ sinfo -= ([SI_CLOSURE, SI_SKILLARG, SI_NUMBER_ENEMIES, SI_NUMBER_FRIENDS,
+ SI_DISTANCE, SI_WIDTH, SI_DEPTH, SI_TESTFLAG, SI_ENEMY,
+ SI_FRIEND, SP_NAME, SP_SHOW_DAMAGE, SP_REDUCE_ARMOUR,
+ SP_PHYSICAL_ATTACK, SP_RECURSIVE, SP_NO_ENEMY,
+ SP_NO_ACTIVE_DEFENSE, SP_GLOBAL_ATTACK ]);
+ else
+ {
+ tell_object(this_object(),sprintf(
+ "\n**** ACHTUNG - FEHLER ***\n"
+ "Deine Skills enthalten einen defekten Skill %O:\n"
+ "Bitte lass dies von einem Erzmagier ueberpruefen.\n",
+ skillname));
+ }
+}
+
+// Updatet und repariert ggf. Skillmappings in Spielern
+protected void FixSkills() {
+
+ // nur bei genug rechenzeit loslegen
+ if (get_eval_cost() < 750000) {
+ call_out(#'FixSkills, 1);
+ return;
+ }
+ // wenn gar keine Skills da (?): InitSkills() rufen.
+ mapping allskills = Query(P_NEWSKILLS, F_VALUE);
+ if (!mappingp(allskills) || !sizeof(allskills)) {
+ InitSkills();
+ return;
+ }
+
+ // Die Fallthroughs in diesem switch sind voll Absicht!
+ switch(QueryProp(P_SKILLSVERSION)) {
+ // bei Version 0 und 1 das gleiche tun
+ case 0: // von 0 auf 1
+ case 1: // von 1 auf 2
+ foreach(string gilde, mapping skills: allskills) {
+ if (!stringp(gilde)) {
+ // sollte nicht vorkommen - tat aber... *seufz*
+ m_delete(skills, gilde);
+ continue;
+ }
+ walk_mapping(skills, #'FixSkillV1);
+ }
+ // allg. Waffenskills aktivieren, einige alte Spieler haben die noch
+ // nicht.
+ set_weapon_skills();
+ // Speicherflag fuer die Versionsprop muss noch gesetzt werden.
+ Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
+ // Version ist jetzt 2.
+ SetProp(P_SKILLSVERSION, 2);
+ // Fall-through
+ case 2:
+ // gibt es noch nicht, nichts machen.
+ //SetProp(P_SKILLSVERSION, 3);
+ // Fall-through, ausser es sind zuwenig Ticks da!
+ if (get_eval_cost() < 750000)
+ break;
+ }
+ // Falls noch nicht auf der aktuellen Version angekommen, neuer callout
+ if (QueryProp(P_SKILLSVERSION) < CURRENT_SKILL_VERSION)
+ call_out(#'FixSkills, 2);
+}
+
+protected void updates_after_restore(int newflag) {
+ //Allgemeine Waffenskills aktivieren, wenn noetig
+ // Wird nun von InitSkills bzw. FixSkills uebernommen, falls noetig.
+ if (newflag) {
+ InitSkills();
+ }
+ else if (QueryProp(P_SKILLSVERSION) < CURRENT_SKILL_VERSION) {
+ // Falls noetig, Skills fixen/updaten. *grummel*
+ FixSkills();
+ }
+ // Prop gibt es nicht mehr. SAVE-Status loeschen.
+ Set(P_GUILD_PREVENTS_RACESKILL,SAVE,F_MODE_AD);
+}
+
+// Standardisierte Nahkampf-Funktion fuer alle Nahkampf-Waffenarten
+protected mapping ShortRangeSkill(object me, string sname, mapping sinfo)
+{
+ int val, w;
+ object enemy;
+
+ if (!mappingp(sinfo) || !objectp(sinfo[P_WEAPON]))
+ return 0;
+
+ w = ([WT_KNIFE : 8,
+ WT_SWORD : 5,
+ WT_AXE : 4,
+ WT_SPEAR : 6,
+ WT_CLUB : 1,
+ WT_WHIP : 9,
+ WT_STAFF : 7])[sinfo[P_WEAPON]->QueryProp(P_WEAPON_TYPE)];
+
+
+ val = sinfo[SI_SKILLABILITY]*(sinfo[P_WEAPON]->QueryProp(P_WC)*
+ (w*QueryAttribute(A_DEX)+
+ (10-w)*QueryAttribute(A_STR))/700)
+ /MAX_ABILITY;
+
+ if (val > 85) {
+ log_file("WEAPON_SKILLS", sprintf("%s: Zu hoher Schaden von: "
+ +"TO: %O, TI: %O, PO: %O, val: %d, A_DEX: %d, A_STR: %d, "
+ +"P_WEAPON: %O, P_WC: %d\n", dtime(time()),
+ this_object(), this_interactive(),
+ previous_object(), val,
+ QueryAttribute(A_DEX),
+ QueryAttribute(A_STR), sinfo[P_WEAPON],
+ sinfo[P_WEAPON]->QueryProp(P_WC)));
+ val = 85;
+ }
+
+ /*
+ Der zusätzliche Schaden der allgemeinen Waffenskills berechnet
+ sich wie folgt:
+
+ sinfo[SI_SKILLABILITY)* (P_WC * ( X ) / 800) / MAX_ABILITY
+
+ Dabei beruecksichtigt X je nach Waffentyp in unterschiedlicher
+ Gewichtung die Werte fuer Geschicklichkeit und Staerke.
+
+ X ==
+
+ Messer : 8*A_DEX + 2*A_STR
+ Schwert : 5*A_DEX + 5*A_STR
+ Axt : 4*A_DEX + 6*A_STR
+ Speer : 6*A_DEX + 4*A_STR
+ Keule : 1*A_DEX + 9*A_STR
+ Peitsche : 9*A_DEX + 1*A_STR
+ */
+
+ sinfo[SI_SKILLDAMAGE]+=val;
+
+
+ /* Lernen: Wird immer schwieriger, nur bei jedem 20. Schlag im Schnitt,
+ * und nur dann, wenn der Gegner auch XP gibt. */
+ if (random(MAX_ABILITY+1)>sinfo[SI_SKILLABILITY] && !random(10))
+ {
+ enemy=sinfo[SI_ENEMY];
+ if (objectp(enemy) && (enemy->QueryProp(P_XP)>0))
+ {
+ object ausbilder;
+ // log_file("humni/log_wurm","Haut: %s und zwar %s, mit xp %d\n",geteuid(this_object()),to_string(enemy),enemy->QueryProp(P_XP));
+ LearnSkill(sname, random(5), 150);
+ // Gibt es einen Ausbilder?
+ if (QueryProp(P_WEAPON_TEACHER) &&
+ ausbilder=find_player(QueryProp(P_WEAPON_TEACHER)))
+ {
+ // Ist der Ausbilder anwesend?
+ if (present(ausbilder,environment()))
+ {
+ // Ausbilder und Azubi muessen dieselbe Waffe haben.
+ //string wt_aus,wt_azu;
+ object waf_aus,waf_azu;
+
+ waf_azu=QueryProp(P_WEAPON);
+ waf_aus=call_other(ausbilder,"QueryProp",P_WEAPON);
+
+ //wt_azu=call_other(waf_azu,"QueryProp",P_WEAPON_TYPE);
+ //wt_aus=call_other(waf_aus,"QueryProp",P_WEAPON_TYPE);
+ //if (wt_azu==wt_aus)
+ if (objectp(waf_aus) && objectp(waf_azu) &&
+ (string)waf_aus->QueryProp(P_WEAPON_TYPE)
+ == (string)waf_azu->QueryProp(P_WEAPON_TYPE))
+ {
+ // Bonus von bis zu 5 Punkten
+ //log_file("humni/log_azubi",
+ // sprintf("Azubi %O und Ausbilder %O : Waffentypen %s und %s, gelernt\n",this_object(),
+ // ausbilder, wt_azu, wt_aus));
+ LearnSkill(sname,random(6),150);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ Die Schwierigkeit liegt bei 150, so dass
+ ein Lvl. 1 Spieler maximal 15% Skill
+ usw...
+ lernen kann. (Genaue Tabelle in /std/living/skills bei LimitAbility)
+ */
+
+ return sinfo;
+}
+
+
+// Standardisierte Fernkampf-Funktion fuer alle Fernkampf-Waffenarten
+
+// *** noch deaktiviert ***
+
+protected mapping LongRangeSkill(object me, string sname, mapping sinfo, int dam)
+{ int abil,val;
+
+ if (!mappingp(sinfo) || !dam || !objectp(sinfo[P_WEAPON]) ||
+ (sinfo[P_WEAPON]->QueryProp(P_SHOOTING_WC))<5)
+ return 0;
+
+ abil=sinfo[SI_SKILLABILITY]+sinfo[OFFSET(SI_SKILLABILITY)];
+ val=dam*abil/MAX_ABILITY;
+ val=val/2+random(val/2+1);
+ val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+ sinfo[SI_SKILLDAMAGE]+=val;
+
+ if (random(MAX_ABILITY+1)>sinfo[SI_SKILLABILITY] && random(50)==42)
+ LearnSkill(sname, 1, 150);
+
+ return sinfo;
+}
+
+
+
+// Die einzelnen Waffenskills rufen dann nur die Standard-Funktion auf.
+
+protected mapping StdSkill_Fight_axe(object me, string sname, mapping sinfo)
+{
+ return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_club(object me, string sname, mapping sinfo)
+{
+ return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_knife(object me, string sname, mapping sinfo)
+{
+ return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_spear(object me, string sname, mapping sinfo)
+{
+ return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_sword(object me, string sname, mapping sinfo)
+{
+ return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_whip(object me, string sname, mapping sinfo)
+{
+ return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_staff(object me, string sname, mapping sinfo)
+{
+ return ShortRangeSkill(me, sname, sinfo);
+}
+
+
+
+
+// Die Fernwaffenskills sind Munitionsabhaengig
+
+// *** noch deaktiviert ***
+
+protected mapping StdSkill_Shoot_arrow(object me, string sname, mapping sinfo)
+{
+ return LongRangeSkill(me, sname, sinfo, 40);
+}
+
+protected mapping StdSkill_Shoot_bolt(object me, string sname, mapping sinfo)
+{
+ return LongRangeSkill(me, sname, sinfo, 40);
+}
+
+protected mapping StdSkill_Shoot_dart(object me, string sname, mapping sinfo)
+{
+ return LongRangeSkill(me, sname, sinfo, 20);
+}
+
+protected mapping StdSkill_Shoot_stone(object me, string sname, mapping sinfo)
+{
+ return LongRangeSkill(me, sname, sinfo, 40);
+}
+
+protected mixed _query_localcmds() {
+ return ({ ({"spruchermuedung","enable_spell_fatigue_compat",0,0}) });
+}
+
+
+
+// *** Kompatibilitaetsmodus fuer Spellfatigues (Emulation 2s-Alarmzeit) ***
+
+/** Speichert eine Spellfatigue von <duration> Sekunden fuer <key>.
+ * Ist hier nur fuer den Spellfatigue-Compat-mode.
+ * <key> darf 0 sein und bezeichnet das globale Spellfatigue.
+ * Rueckgabewert: Ablaufzeit der gesetzten Sperre
+ -1, wenn noch eine nicht-abgelaufene Sperre auf dem <key> lag.
+ 0, wenn duration 0 ist.
+ */
+public varargs int SetSpellFatigue(int duration, string key) {
+
+ // 0 sollte nie eine Sperre bewirken, auch nicht im compat mode.
+ if (!duration) return 0;
+
+ if (spell_fatigue_compat_mode) {
+ // Spell-Fatigues auf HBs synchronisieren (2s-Alarmzeit-Emulation).
+ // Aufrunden auf ganzzahlige Vielfache von __HEART_BEAT_INTERVAL__
+ if (duration % __HEART_BEAT_INTERVAL__)
+ ++duration;
+
+ // Startpunkt des Delay soll Beginn der aktuellen Kampfrunde (HB-Zyklus)
+ // sein, ggf. um max. eine Sekunde zurueckdatieren.
+ // (spell_fatigue_compat_mode hat die Zeit des letzten HB-Aufrufs)
+ // Falls durch irgendein Problem (z.B. sehr hohe Last), der letzte HB
+ // laenger als 1s zurueckliegt, funktioniert das natuerlich nicht, aber
+ // bei fatigue+=spell_fatigue_compat_mode kann der Spieler zuviel Zeit
+ // einsparen.
+ if (time() > spell_fatigue_compat_mode)
+ --duration; //1s zurueckdatieren
+ }
+
+ return ::SetSpellFatigue(duration, key);
+}
+
+/** Befehlsfunktion fuer Spieler um den Spellfatigue-Kompatibilitaetsmodus
+ * umzuschalten.
+ */
+public int enable_spell_fatigue_compat(string cmd) {
+ if (QueryProp(P_LAST_COMBAT_TIME) + 600 > time()) {
+ write(break_string(
+ "Im Kampf oder kurz nach einem Kampf kannst Du nicht zwischen "
+ "alter und neuer Spruchermuedung umschalten.\n"
+ "Momentan benutzt Du die "
+ + (spell_fatigue_compat_mode ? "alte (ungenauere)" : "neue (normale)")
+ + " Spruchermuedung.",78,0,BS_LEAVE_MY_LFS));
+ return 1;
+ }
+
+ if (cmd=="alt") {
+ spell_fatigue_compat_mode=time();
+ write(break_string(
+ "Alte Spruchermuedung wurde eingeschaltet. Alle Ermuedungspausen "
+ "zwischen Spruechen werden auf Vielfache von 2s aufgerundet und "
+ "beginnen in der Regel am Anfang Deiner Kampfrunde."));
+ return 1;
+ }
+ else if (cmd=="neu" || cmd=="normal") {
+ spell_fatigue_compat_mode=0;
+ write(break_string(
+ "Normale Spruchermuedung wurde eingeschaltet. Alle Ermuedungspausen "
+ "zwischen Spruechen werden sekundengenau berechnet."));
+ return 1;
+ }
+
+ notify_fail(break_string(
+ "Moechtest Du die alte oder die neue Spruchermuedung?\n"
+ "Momentan benutzt Du die "
+ + (spell_fatigue_compat_mode ? "alte (ungenauere)" : "neue (normale)")
+ + " Spruchermuedung.",78,0,BS_LEAVE_MY_LFS));
+ return 0;
+}
+
+/** Speichert die Zeit des letztes Heartbeats.
+ */
+protected void heart_beat() {
+ if (spell_fatigue_compat_mode)
+ spell_fatigue_compat_mode = time();
+}
+
+static int _set_guild_level(int num)
+{ string gilde;
+ mapping levels;
+
+ if ( !(gilde=QueryProp(P_GUILD)) )
+ return 0;
+
+ if ( !mappingp(levels=Query(P_GUILD_LEVEL)) )
+ levels=([]);
+
+ levels[gilde]=num;
+ Set(P_GUILD_LEVEL,levels);
+ GMCP_Char( ([P_GUILD_LEVEL: num]) );
+
+ return num;
+}
+
+static int _query_guild_level()
+{ string gilde;
+ mapping levels;
+
+ if ( !(gilde=QueryProp(P_GUILD)) )
+ return 0;
+
+ if ( !mappingp(levels=Query(P_GUILD_LEVEL)) )
+ return 0;
+
+ return levels[gilde];
+}
+
+static string _set_guild_title(string t)
+{ string gilde;
+ mapping titles;
+
+ if ( !(gilde=QueryProp(P_GUILD)) )
+ return 0;
+
+ if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
+ titles=([]);
+
+ titles[gilde]=t;
+ Set(P_GUILD_TITLE,titles);
+ GMCP_Char( ([P_GUILD_TITLE: t]) );
+ return t;
+}
+
+static string _query_guild_title()
+{ string gilde,t;
+ object g;
+ mapping titles;
+
+ if ( !(gilde=QueryProp(P_GUILD)) )
+ return 0;
+
+ if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
+ titles=([]);
+
+ t=titles[gilde];
+ if ( !t && query_once_interactive(this_object())
+ && objectp(g=find_object("/gilden/"+gilde)) )
+ {
+ g->adjust_title(this_object());
+ SetProp(P_TITLE,0);
+
+ if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
+ return 0;
+
+ t=titles[gilde];
+ }
+
+ return t;
+}
+
+
+static string _set_guild(string gildenname)
+{ object pre;
+
+ if (!objectp(pre=previous_object()))
+ return 0;
+
+ if ( pre!=this_object() // Das Lebewesen selber darf die Gilde setzen,
+ && object_name(pre)!=GUILDMASTER // der Gildenmaster auch
+ && (!this_player()
+ || this_player() != this_interactive()
+ || !IS_ARCH(this_player())
+ )
+ )
+ return 0;
+
+ Set(P_GUILD,gildenname);
+ GMCP_Char( ([P_GUILD: gildenname]) );
+ return gildenname;
+}
+
+static string _query_guild()
+{ string res;
+
+ if ( !(res=Query(P_GUILD)) && query_once_interactive(this_object()) )
+ {
+ // Spieler, die keiner Gilde angehoeren, gehoeren zur Abenteurergilde
+ if ( !(res=QueryProp(P_DEFAULT_GUILD)) )
+ return DEFAULT_GUILD;
+ else
+ Set(P_GUILD,res);
+ return res;
+ }
+
+ return res;
+}
+
+static string _query_title()
+{ string ti;
+
+ if ( stringp(ti=Query(P_TITLE)) )
+ return ti;
+
+ return QueryProp(P_GUILD_TITLE);
+}
+
+static string _set_title(string t)
+{
+ Set(P_TITLE, t, F_VALUE);
+ GMCP_Char( ([P_TITLE: t]) );
+ return t;
+}
+
diff --git a/std/player/soul.c b/std/player/soul.c
new file mode 100644
index 0000000..438f2c6
--- /dev/null
+++ b/std/player/soul.c
@@ -0,0 +1,3218 @@
+// MorgenGrauen MUDlib
+//
+// player/soul.c -- Die Seele des Spielers
+//
+// $Id: soul.c 9527 2016-03-12 11:37:54Z Arathorn $
+
+// Letzte Aenderung vom 08.09.95 Wargon
+
+// Set TabStop to 2 characters
+
+/* Version 1.41 MG, September 95
+ - Bei "frage" und "antworte" Test auf P_PERM_STRING fuer Sprachflueche.
+ - bugfix bei "schweige" (nahm keine Adverbien an)
+ */
+
+/* Version 1.4 MG, August 95
+ - Hilfefunktion eingebaut, siehe auch soulhelp.c
+ - einige kleinere Aenderungen erst jetzt durch eine neue Version gewuerdigt
+ - neue Verben, wie eigentlich fast immer :>
+ - typos und bugs gefixed (und neue eingebaut...)
+ - Funktion zur Abfrage von Adverbien von aussen eingebaut (Hallo, Anthea :>)
+ - so schlimm kann das doch nicht sein, einen TabStop von 2 zu nehmen, fuer
+ dieses eine file, alles andere zerlegt meine Formatierung immer so :<
+ - koenntet ihr bitte mal oben das "Letzte Aenderung" aendern, wenn ihr
+ irgendwo was aendert?
+ */
+
+/* Version 1.35 MG, Dezember 94
+ - Verben
+ - Aenderung des Parsings der quoted adverbs
+ - rknuddel ignorieren wird vom normalen ignore uebernommen
+ */
+
+/* Version 1.32 MG, Juli 94
+ - einige Verben
+ - ignorieren des rknuddel
+ */
+
+/* Version 1.31 MG, Mai 94
+ - einige Verben
+ */
+
+/* Version 1.3 MG, Mai 94
+ - quoted adverbs GEHEN jetzt
+ - ParseRest / ParseRemote neu geschrieben
+ */
+
+/* Version 1.21 MG, April 94
+ - quoted adverbs
+ */
+
+/* Danke an Angus fuer den Text von "liebe" */
+
+/* Version 1.2 MG, Januar 94
+ - Umstellung der Feelings von vielen kleinen Funktionen auf eine grosse,
+ damit der Funktionsoverhead wegfaellt.
+ - neue Ausgabe der Adverbien, mit more und nur noch eines pro Zeile
+ - mal wieder neue Verben.
+ - und das neue Standardadverb "jofi" :)
+ */
+
+/* Version 1.1 MG, November 93
+ Aenderungen:
+ - Ich habe "alle(n)" eingebaut. Die Verwaltung steht, man kann jetzt
+ Verben die Moeglichkeit "alle" geben (Bsp. "wink alle").
+ - inzwischen wieder einige Verben mehr, aber muss ich das noch
+ erwaehnen?
+ - (HASS) Rumata hat mein schoenes System fuer die Ausgabestrings
+ verkompliziert. Statt &a steht da jetzt z.B. @@adverb@@, was in
+ einer weiteren unnoetigen Funktion resultierte. Naja.
+ Highlander ryn Tahar
+ */
+
+/*
+ Ha! Ich nehme jetzt die erste ganze Versionsnummer fuer mich in Anspruch :)
+ So, heute ist's geschafft (Ich auch...). Ich bin fertig - ich mach nur
+ noch neue Verben rein, wenn Ideen kommen.
+ Gegeben an Dienstag, dem 22. Juni 1993 im heimatlichen Horst.
+ Highlander ryn Tahar.
+ P.S.: Kopiere sich das Ding, wer will - unter folgenden Bedingungen:
+ - Den Goettern von MorgenGrauen und
+ - Highlander@TAPPMud Bescheid sagen und ausserdem
+ *seufz* nehmt Highlander hier in MorgenGrauen!! -HrT
+ Ha! Ihr koennt wieder TAPPMud nehmen :> Aber sagt's ruhig in MG. -HrT
+ - entweder den ganzen Schwall hier drinlassen oder mich mit einem
+ neuen Text erwaehnen.
+ Das Ganze unter der Voraussetzung, dass ueberhaupt mal jemand eine deutsche
+ Seele braucht und sie nicht selber schreiben will :-) (ersparts euch lieber)
+ Highlander ryn Tahar.
+
+ **Arbeit:
+ Einbau von Adverbien, andere Reaktionen bei Geistern als "Wie bitte?",
+ einige neue Verben, einige alte Verben rausgeschmissen.
+ Weil es Probleme mit dem autoloading der playeradverbs gab, wurde
+ ausserdem die soul vom Objekt zum inheritfile fuer die playershell.
+
+ **Ideen zum Weitermachen:
+ - (verb) alle, z.B. tritt alle -- Moeglichkeit eingebaut
+ - Geisterverben mit Adverb
+
+ Version 1.0 fuer MorgenGrauen Highlander Mai/Juni 93
+ */
+
+
+/* Hier ist sie nun, die DEUTSCHE version der soul,
+ viel Spass damit.
+ Poietix Mai 1992
+ Vers.: 0.4 fuer JRMud
+ P.S. bitte meckert nicht dran rum , sondern verbessert und
+ erweitert sie.
+
+ Olpp November 1992
+ Vers.: 0.5 fuer MorgenGrauen
+
+ He, Olpp, schreibt man nicht die neueste Aenderung UEBER die alten?
+ *grins*, Highlander
+ */
+#pragma strong_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define SOULHELP "/std/player/soulhelp"
+#define LF "\n"
+#define NOT_SELF 1
+#define NOT_DEAD 1
+
+#define QPP QueryPossPronoun
+#define RETURN return _notify_fail
+#define Return return 0||_notify_fail // netter Trick, muss ich mir merken -HrT
+#define GHOSTCHECK(sel,oth,vic) if (ghost()) { write(sel); say(oth,who||ME);\
+ if (vic) who->Message(vic); return 1; }
+#define HELPCHECK(x) if (str && (str=="-h" || str=="-?" || str=="/h" \
+ || str=="/?" || str=="hilfe"))\
+ { More(SOULHELP->Help(x)); return 1; }
+
+// "schau an" als nicht-Untersuchung. Klappt aber anscheinend nicht, weil
+// ich nicht gegen den GD ankomme. Also besser auskommentiert lassen.
+#ifdef SCHAU_AN
+#undef SCHAU_AN
+#endif
+
+// Anpiepsen mit Text. Im Moment erlaubt, bei Missfallen auskommentieren und
+// in der Hilfe auskommentieren.
+#ifdef WECKE
+#undef WECKE
+#endif
+
+#define WECKE
+
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+#include <thing/properties.h>
+#include <player.h>
+#include <player/comm.h>
+#include <language.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+
+#include <defines.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <class.h>
+
+static object who, ofoo;
+static int for_all, flag, ifoo;
+mapping plr_adverbs;
+static string out_sel, out_vic, out_oth, adverb, sfoo;
+
+private void ParseAdverb(string *words);
+private string convert_string(string str);
+varargs mixed More(string str, int fflag, string returnto);
+string MatchAdverb(string str);
+
+mapping
+QueryStdAdverbs() {
+ return ([
+ "unve" : "unverschaemt",
+ "gutg" : "gutgelaunt",
+ "gutm" : "gutmuetig",
+ "froh" : "froh",
+ "glue" : "gluecklich",
+ "wuet" : "wuetend",
+ "frec" : "frech",
+ "daem" : "daemonisch",
+ "boes" : "boese",
+ "ungl" : "ungluecklich",
+ "lang" : "langsam",
+ "schn" : "schnell",
+ "jamm" : "jammernd",
+ "freu" : "freundlich",
+ "shue" : "schuechtern",
+ "amue" : "amuesiert",
+ "aerg" : "aergerlich",
+ "aner" : "anerkennend",
+ "erst" : "erstaunt",
+ "bitt" : "bitter",
+ "brei" : "breit",
+ "vors" : "vorsichtig",
+ "char" : "charmant",
+ "kalt" : "kalt",
+ "verf" : "verfuehrerisch",
+ "zufr" : "zufrieden",
+ "tief" : "tief",
+ "verz" : "verzweifelt",
+ "drec" : "dreckig",
+ "vert" : "vertraeumt",
+ "uebe" : "ueberzeugt",
+ "frus" : "frustriert",
+ "stra" : "strahlend",
+ "hoff" : "hoffnungsvoll",
+ "unge" : "ungeduldig",
+ "unsi" : "unsinnigerweise",
+ "unsc" : "unschuldig",
+ "unwi" : "unwissend",
+ "iron" : "ironisch",
+ "wiss" : "wissend",
+ "gema" : "gemaechlich",
+ "sehn" : "sehnsuechtig",
+ "laut" : "laut",
+ "lieb" : "liebevoll",
+ "froe" : "froehlich",
+ "dank" : "dankbar",
+ "natu" : "natuerlich",
+ "gedu" : "geduldig",
+ "perf" : "perfekt",
+ "vers" : "verspielt",
+ "hoef" : "hoeflich",
+ "stol" : "stolz",
+ "frag" : "fragend",
+ "rupp" : "ruppig",
+ "trau" : "traurig",
+ "vera" : "veraechtlich",
+ "scha" : "schamlos",
+ "erns" : "ernst",
+ "schu" : "schuechtern",
+ "zaer" : "zaertlich",
+ "sanf" : "sanft",
+ "entg" : "entgeistert",
+ "heim" : "heimtueckisch",
+ "gela" : "gelangweilt",
+ "wild" : "wild",
+ "jofi" : "wie Jof, wenn er mal nicht idlet",
+ ]);
+}
+
+mapping
+QueryAdverbs() {
+ if (extern_call())
+ return deep_copy(plr_adverbs);
+ return plr_adverbs;
+}
+
+string
+MatchAdverb(string a) {
+ ParseAdverb(explode(a," "));
+ return adverb;
+}
+
+// Verwaltungsroutinen
+
+static void
+add_soul_commands() {
+ if (!plr_adverbs)
+ plr_adverbs=([]);
+ add_action("SoulComm", "", 1);
+}
+
+static int
+verben_liste() {
+ More(SOULHELP->Help());
+ return 1;
+}
+
+#define ghost() QueryProp(P_GHOST)
+#define frog() QueryProp(P_FROG)
+#define capname() capitalize(name())
+#define gname() (ghost()?(frog()?"Der Geist eines Frosches"\
+ :"Der Geist von "+capname())\
+ :capname())
+
+varargs private void
+ParseRest(string arg, mixed extra) {
+ string wer,wie,*words,quotea;
+ int num,bis;
+ who = for_all = adverb = 0;
+ if (!arg) return;
+ if (extra)
+ if (!pointerp(extra)) {
+ if (sscanf(arg, extra+" %s", wie)==1)
+ arg=wie;
+ }
+ else
+ for (bis=sizeof(extra),num=0; num<bis; num++)
+ if (sscanf(arg, extra[num]+" %s", wie)==1)
+ arg=wie;
+
+ if ((bis=strstr(arg, "/"))>=0)
+ quotea=arg[bis..],arg=arg[0..bis-1];
+ quotea=quotea||"",arg=arg||"";
+
+ words=explode(implode(explode(arg, ","), " und"), " ");
+ if (!sizeof(words)) return;
+ if (sizeof(words) && (words[0]=="alle" || words[0]=="allen"))
+ for_all=1,wer=words[0],words=words[1..];
+ if (!for_all) { /* noch kein Opfer */
+ wer=match_living(lower_case(words[0]));
+ if (stringp(wer)) who=present(wer, environment(ME));
+ if (!who) who=present(words[0], environment(ME));
+ if (who && who->QueryProp(P_INVIS)) who=0;
+ }
+ if (who && sizeof(words))
+ words=words[1..]; /* Opfer gefunden - wenn's eines gibt */
+ words+=explode(quotea, " ");
+ words-=({""});
+ if (sizeof(words)) ParseAdverb(words);
+}
+
+private int
+ParseRemote(string arg) {
+ string wer,*words;
+
+ adverb = 0; // Adverb vom letzten Mal keinesfalls wiederverwenden. ;-)
+
+ if (!stringp(arg) || !sizeof(arg)) return 0;
+
+ words=explode(arg," ");
+
+ mixed liv = match_living(lower_case(words[0]));
+ if (stringp(liv))
+ who=find_player(liv);
+
+ if (who) {
+ // Ziel ist ein Spieler.
+ if (!who->QueryProp(P_INVIS) || IS_WIZARD(ME))
+ {
+ // Spieler ist nicht Invis oder ich bin Magier.
+ string nam = (query_once_interactive(ME) ? getuid() :
+ lower_case(name(RAW)));
+ if (query_verb()[0..5]=="rknudd" &&
+ who->TestIgnore(nam+".rknuddel") )
+ {
+ // ich oder das Kommando werde ignoriert.
+ write(who->Name(WER)+" ignoriert Deinen Knuddelversuch.\n");
+ return 1;
+ }
+ }
+ else
+ // Spieler ist invis und ich bin kein Magier.
+ who = 0;
+ }
+ // kein eingeloggter und sichtbarer Spieler. Vielleicht ein NPC? (BTW: kein
+ // else if, weil im if fuer Spieler oben who genullt werden kann und dann
+ // nochmal nach nem NPC gesucht werden soll.)
+ if (!who) {
+ wer = match_living(lower_case(words[0]));
+ if(stringp(wer))
+ who=present(wer,environment(ME));
+ if (!who) who=present(words[0], environment(ME));
+ if (who && who->QueryProp(P_INVIS)) who=0;
+ }
+
+ if (!who || sizeof(words)==1) return 0;
+ words=words[1..];
+ ParseAdverb(words);
+ return(0);
+}
+
+/**
+ Gibt den passenden Adverb-Text zu einem key zurueck
+ \param s Danach wird in der Adverbien-Liste gesucht
+ \param fuzzy
+ \return Der gefundene Adverbientext oder 0
+ */
+varargs string GetPlayerAdverb( string s, int fuzzy ) {
+ int i, j;
+ string *search_pattern,
+ *search_result,
+ result;
+
+ // Erstmal gucken, ob der String direkt gefunden werden kann
+ // das geht am schnellsten
+ result = QueryStdAdverbs()[s] || plr_adverbs[s];
+
+ // Wenn noch kein Ergebnis gefunden, und man unscharf suchen will
+ if ( fuzzy && !result) {
+
+ // Suchmuster fuer das Intersect erzeugen
+ search_pattern=({s});
+
+ j = sizeof(s)-1;
+ for ( i=2; i < j ;i++) {
+ search_pattern += ({s[0..<i]});
+ }
+
+ // Intersect zwischen allen bekannten Abkuerzungen und Search-Pattern
+ // erzeugen. Dieses wird dann gleichzeitig nach Laenge sortiert
+ // genauester Treffer = String mit groesster Laenge
+ search_result = sort_array(
+ (m_indices(QueryStdAdverbs()) | m_indices(plr_adverbs))&search_pattern,
+ #'>);
+
+ // Adverb zum genauesten Treffer zurueckgeben
+ if (sizeof(search_result))
+ result = QueryStdAdverbs()[search_result[0]] ||
+ plr_adverbs[search_result[0]];
+ }
+
+ return result;
+}
+
+/**
+ Parst die Adverbienparameter fuer Verben und speichert die
+ passende Textausgabe in der globalen Variable "adverb"
+ \param words Array mit den zu parsenden Adverbien-Strings
+*/
+private void
+ParseAdverb(string *words) {
+ int num,andsign,bis;
+ string qadv,*adv,cnt;
+
+ adv=({});
+ qadv=0;
+
+ bis=sizeof(words);
+ // Sonderfall Gequotetes Adverb (alles nach dem Quote) speichern und aus
+ // Words rausschneiden.
+ for (num=0; num<bis; num++)
+ if (words[num][0..0]=="/") {
+ words[num]=words[num][1..];
+ qadv=implode(words[num..], " ");
+ words=words[0..num-1];
+ break;
+ }
+
+ // Es kann sein, dass vor dem Quote noch eine und steht. Das wird jetzt auch
+ // noch entfernt, muss aber spaeter wieder eingefuegt werden.
+ if (sizeof(words) && words[<1]=="und") {
+ words=words[0..<2];
+ andsign=1;
+ }
+
+ // Weitersuchen?
+ if (bis=sizeof(words))
+ for (num=0; num<bis; num+=2)
+ adv+=({GetPlayerAdverb(words[num], 1)});
+ cnt=CountUp(adv-({0}));
+
+ // Ausgabe zusammenbauen
+ if (andsign)
+ adverb=CountUp((sizeof(adv) ? adv : ({}))+(qadv ? ({qadv}) : ({})));
+ else if (sizeof(cnt) && sizeof(qadv))
+ adverb = cnt + " " + qadv;
+ else if (sizeof(qadv))
+ adverb = qadv;
+ else if (sizeof(cnt))
+ adverb = cnt;
+ if (adverb=="") adverb=0;
+}
+
+private mixed MixedOut(int casus) {
+ object *envs,*vics;
+ string *names,out,aufz;
+ int count,msg;
+
+ for_all=0;
+ vics=({});
+ names=({});
+ envs=all_inventory(environment())-({this_player()});
+ if (!(count=sizeof(envs)))
+ RETURN("Nichts und niemand da. Schau Dich naechstes Mal besser um.\n");
+ for ( ; count--; )
+ if (living(envs[count]) && !envs[count]->QueryProp(P_INVIS)) {
+ vics+=({envs[count]});
+ names+=({envs[count]->name(casus)});
+ }
+ if (!sizeof(vics))
+ RETURN("Keiner da. Schau Dich naechstes Mal besser um.\n");
+ aufz=CountUp(names);
+ for (count=sizeof(vics); count--;)
+ {
+ out=implode(explode(out_vic, "@@alle@@"),aufz);
+ out = regreplace( out, "\\<"+vics[count]->name(casus)+"\\>",
+ capitalize(vics[count]->QueryDu(casus)), 0 );
+
+ msg=vics[count]->ReceiveMsg(convert_string(out),MT_COMM,MA_EMOTE,
+ 0,this_object());
+ switch(msg)
+ {
+ case MSG_DELIVERED:
+ case MSG_BUFFERED:
+ break;
+ case MSG_IGNORED:
+ case MSG_VERB_IGN:
+ case MSG_MUD_IGN:
+ write(vics[count]->Name()+" ignoriert Dich oder diesen Befehl.\n");
+ break;
+ default:
+ write(vics[count]->Name()+" konnte Dich gerade nicht lesen.\n");
+ }
+ }
+ write(break_string(convert_string(implode(explode(out_sel,"@@alle@@"),aufz)
+ +LF), 78));
+ return 1;
+}
+
+varargs private int
+CheckLife(int no_self,int no_dead, string no_self_text, string no_dead_text) {
+ if (who && living(who) && who!=this_player()) return 0;
+ if (no_self && who && who==this_player()) {
+ if (no_self_text)
+ write(no_self_text+LF);
+ else
+ write("Mach das mit anderen, nicht mit Dir selber.\n");
+ return 1;
+ }
+ if (who && !living(who) && no_dead) {
+ if (no_dead_text)
+ write(no_dead_text+LF);
+ else
+ write("Das darfst Du nur mit Lebewesen.\n");
+ return 2;
+ }
+ if (!who) {
+ write("Schau Dich erst mal um - das angegebene Objekt ist nicht da.\n");
+ return 3;
+ }
+ return(0); //non-void. Fall-through, alles OK.
+}
+
+private string
+convert_string(string str) {
+ /* Ich bin unschuldig, ich hatte das viel einfacher und schoener :)
+ Rumata wollte das so ;) -HrT */
+ str = implode( explode( str, "@@name@@" ), capname() );
+ str = implode( explode( str, "@@gname@@" ), gname() );
+ str = implode( explode( str, "@@wer@@" ),
+ (who?capitalize(who->name(WER,2)||""):"" ));
+ str = implode( explode( str, "@@ wen@@" ),
+ (who?" "+who->name(WEN,2):""));
+ str = implode( explode( str, "@@ wem@@" ),
+ (who?" "+who->name(WEM,2):""));
+ str = implode( explode( str, "@@wen@@" ),
+ (who?who->name(WEN,2):""));
+ str = implode( explode( str, "@@wem@@" ),
+ (who?who->name(WEM,2):""));
+ str = implode( explode( str, "@@wessen@@" ),
+ (who?who->name(WESSEN,2):""));
+ str = implode( explode( str, "@@adverb@@" ),
+ (adverb?" "+adverb:"") );
+ return str;
+}
+
+private int
+FeelIt() {
+ int msg, flg;
+
+ flg = MSGFLAG_SOUL;
+ if (query_verb() && (query_verb()[0..3]=="frag" || query_verb()[0..3]=="antw"))
+ flg |= MSGFLAG_SAY;
+ if (query_verb() && (query_verb()[0..5]=="rknudd" || query_verb()=="rwink"))
+ flg |= MSGFLAG_REMOTE;
+
+ // NPC haben keine TM-Hist (comm.c). Leider erben aber div. Magier die Soul
+ // (trotzdem sie in /std/player/ liegt) in ihren NPC... *fluch*
+ if (query_once_interactive(ME))
+ _recv(who, break_string(convert_string(out_sel),78), flg);
+ else
+ tell_object(ME, break_string(convert_string(out_sel),78));
+
+ if (out_vic && who) {
+ if (query_once_interactive(who)) {
+ msg=who->Message( break_string(convert_string( out_vic ),78), flg);
+ if (msg==-1)
+ write(who->name()+" ignoriert Dich oder diesen Befehl.\n");
+ } else
+ tell_object(who,break_string(convert_string( out_vic ),78));
+ }
+ if (out_oth)
+ say( break_string(convert_string( out_oth ),78), ({who,this_player()}) );
+ out_sel=out_vic=out_oth=0;
+ return 1;
+}
+
+/**
+ Die Funktion stellt einen Hilfetext zur Verfuegung und listet die
+ definierten Adverbien auf.
+ \param mine 0 = alle Adverbien,
+ 1=nur selbst definierte Adverbien
+ 2=nur die vom System bereitgestellten
+ */
+private int zeige_adverbs(int mine) {
+ mapping adverb_list;
+ string out,s;
+
+ // Parameter auswerten
+ switch (mine){
+ case 1:
+ adverb_list=plr_adverbs;
+ out = "Du hast folgende Adverbien definiert:\n";
+ break;
+ case 2:
+ adverb_list=QueryStdAdverbs();
+ out = "Systemseitig sind folgende Adverbien definiert:\n";
+ break;
+ default:
+ adverb_list=QueryStdAdverbs()+plr_adverbs;
+ out = "Folgende Adverbien stehen Dir zur Verfuegung:\n";
+ }
+
+ out +=" Abk. Adverb\n ====== ======\n";
+
+ if ( sizeof(adverb_list) == 0)
+ out += " keine.\n";
+ else
+ // Ueber alle Elemente der indizies der Adverbienliste gehen
+ foreach ( s : sort_array(m_indices(adverb_list), #'> ) ) {
+ out += break_string(adverb_list[s],78,
+ sprintf(" %-6s ",s),BS_INDENT_ONCE);
+ }
+
+ More(out+"\nWie diese Adverbien benutzt werden ist in <hilfe adverb> "
+ "beschrieben.\n");
+
+ return 1;
+}
+
+varargs static int
+SoulComm(string str, string _verb) {
+ int t_g,t_n,flag;
+ string str1,str2,str3,*taenze,vb;
+ out_sel=out_vic=out_oth=who=0;
+// if (this_interactive()!=ME) return 0;
+ if (interactive(ME)) str=_unparsed_args(); // NPCs haben das nicht :(
+ if (str=="") str=0;
+ vb=_verb||query_verb();
+ if (sizeof(vb)>1 && vb[<1]=='e' && vb!="noe") vb=vb[0..<2];
+ sfoo = 0;
+ switch (vb) {
+ /**************** Aechzen ***************/
+ case "aechz":
+ HELPCHECK("aechz");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Aechze wie?\n");
+ out_sel="Du aechzt@@adverb@@.";
+ out_oth="@@gname@@ aechzt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Anschmiegen ***************/
+ case "schmieg":
+ HELPCHECK("schmieg");
+ ParseRest(str);
+ if (!who)
+ Return("An wen willst Du Dich anschmiegen?\n");
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Das geht doch nicht.",
+ "Nein, das macht keinen Spass. Lebt ja nicht mal."))
+ return 1;
+ out_sel="Du schmiegst Dich@@adverb@@ an@@ wen@@ an.";
+ out_vic="@@gname@@ schmiegt sich@@adverb@@ an Dich.";
+ out_oth="@@gname@@ schmiegt sich@@adverb@@ an@@ wen@@ an.";
+ return FeelIt();
+
+ /**************** Antworten ***************/
+ case "antwort":
+ HELPCHECK("antwort");
+ if (!str)
+ Return("Antworte [WEM] WAS?\n");
+ ParseRest(str);
+ if (!who)
+ str1=capitalize(str);
+ else
+ if (sscanf(str,"%s %s",str1,str1)!=2)
+ Return("Antworte was?\n");
+ else
+ str1=capitalize(str1);
+ out_sel="Du antwortest@@ wem@@: "+str1;
+ /* Sprachflueche beruecksichtigen -Wargon, 8. 9. 95 */
+ if (QueryProp(P_PERM_STRING))
+ str1 = call_other(QueryProp(P_PERM_STRING),"permutate_string",str1)||"";
+ if (who) out_vic="@@gname@@ antwortet Dir: "+str1;
+ out_oth="@@gname@@ antwortet@@ wem@@: "+str1;
+ return FeelIt();
+
+ /**************** Applaudieren ***************/
+ case "applaudier":
+ HELPCHECK("applaudier");
+ GHOSTCHECK("Deine Haende fahren durcheinander durch - war wohl nix.\n",
+ gname()+" will applaudieren, aber "+QPP(FEMALE,WER,PLURAL)
+ +" Haende sausen\ndurcheinander durch.\n", 0);
+ if (!str) {
+ out_sel="Du applaudierst von ganzem Herzen.";
+ out_oth="@@name@@ gibt eine Runde Applaus.";
+ }
+ else {
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du applaudierst @@alle@@@@adverb@@.";
+ out_vic="@@name@@ applaudiert @@alle@@@@adverb@@.";
+ return MixedOut(WEM);
+ }
+ if (!who && !adverb)
+ Return("Applaudiere wem oder wie oder so aehnlich.\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Nein, das gehoert sich nicht.",
+ "Sachen wird hier nicht applaudiert, OK?"))
+ return 1;
+ out_sel="Du applaudierst@@ wem@@@@adverb@@.";
+ if (who) out_vic="@@name@@ applaudiert Dir@@adverb@@.";
+ out_oth="@@name@@ applaudiert@@ wem@@@@adverb@@.";
+ }
+ return FeelIt();
+
+ /**************** Argln ***************/
+ case "argl":
+ HELPCHECK("argl");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Argle wie?\n");
+ out_sel="Du arglst"+(adverb ? "@@adverb@@." : " ein wenig vor Dich hin.");
+ out_oth="@@gname@@ arglt"
+ +(adverb ? "@@adverb@@." : " ein wenig vor sich hin.");
+ return FeelIt();
+
+ /**************** Aufatmen ***************/
+ case "atm":
+ HELPCHECK("atm");
+ if (!str || sscanf(str,"%sauf",str1)!=1)
+ Return("Atme wie auf?\n");
+ ParseRest(str1);
+ out_sel="Du atmest"+(adverb ? "@@adverb@@" : " erleichtert")+" auf.";
+ out_oth="@@gname@@ atmet"+(adverb ? "@@adverb@@" : " erleichtert")+" auf.";
+ return FeelIt();
+
+ /**************** Begruessen ***************/
+ case "hallo":
+ case "hi":
+ case "begruess":
+ HELPCHECK("begruess");
+ ParseRemote(str);
+ if (!who)
+ Return("Wen willst Du begruessen?\n");
+ if (present(who, environment())) {
+ out_sel="Du heisst @@wen@@@@adverb@@ willkommen.";
+ out_vic="@@gname@@ heisst Dich@@adverb@@ willkommen.";
+ out_oth="@@gname@@ heisst @@wen@@@@adverb@@ willkommen.";
+ }
+ else {
+ out_sel="Du heisst @@wen@@@@adverb@@ aus der Ferne willkommen.";
+ out_vic="@@gname@@ heisst Dich@@adverb@@ aus der Ferne willkommen.";
+ }
+ return FeelIt();
+
+ /**************** Betasten ***************/
+ case "betast":
+ HELPCHECK("betast");
+ ParseRest(str);
+ if (!who)
+ Return("Begrabsche wen?\n");
+ out_sel="Du grabbelst@@adverb@@ an "+who->name(WEM)+" herum.";
+ out_vic="@@gname@@ grabbelt@@adverb@@ an Dir herum.";
+ out_oth="@@gname@@ grabbelt@@adverb@@ an "+who->name(WEM)+" herum.";
+ return FeelIt();
+
+ /**************** Bewundern ***************/
+ case "bewunder":
+ HELPCHECK("bewunder");
+ ParseRest(str);
+ if (!who)
+ Return("Bewundere wen?\n");
+ out_sel="Du bewunderst @@wen@@@@adverb@@.";
+ out_vic="@@gname@@ bewundert Dich@@adverb@@.";
+ out_oth="@@gname@@ bewundert @@wen@@@@adverb@@.";
+ return FeelIt();
+
+ /**************** Bibbern ***************/
+ case "bibber":
+ HELPCHECK("bibber");
+ if (ghost())
+ Return("Als Geist fuehlst Du keine Kaelte.\n");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Bibbere wie?\n");
+ out_sel="Du bibberst@@adverb@@ vor Kaelte.";
+ out_oth="@@name@@ bibbert@@adverb@@ vor Kaelte.";
+ return FeelIt();
+
+ /**************** Bohre Nase ***************/
+ case "bohr":
+ HELPCHECK("bohr");
+ ParseRest(str, ({"nase","in der nase","in nase"}));
+ if (str && str!="nase" && str!="in nase" && str!="in der nase" && !adverb)
+ Return("Bohre wie Nase?\n");
+ out_sel="Du bohrst@@adverb@@ in Deiner Nase.";
+ out_oth="@@gname@@ bohrt@@adverb@@ in der Nase. Igitt! :)";
+ return FeelIt();
+
+ /**************** Brummeln ***************/
+ case "brummel":
+ HELPCHECK("brummel");
+ ParseRest(str);
+ out_sel="Du brummelst"
+ +(adverb ? "@@adverb@@." : (str ? " kaum verstaendlich: "+str+"." : "."));
+ out_oth="@@gname@@ brummelt"
+ +(adverb ? "@@adverb@@." : (str ? " kaum verstaendlich: "+str+"." : "."));
+ return FeelIt();
+
+ /**************** cls ***************/
+ case "cls":
+ HELPCHECK("cls");
+ write("[2J[H");
+ return 1;
+
+ /**************** Daeumchendrehen ***************/
+ case "dreh":
+ HELPCHECK("dreh");
+ if (!str)
+ Return("Drehe was?\n");
+ if(strstr(str,"daeumchen")<0 && strstr(str,"daumen")<0)
+ Return("Drehe was?\n");
+ ParseRest(str,({"daeumchen","daumen"}));
+ out_sel="Du drehst@@adverb@@ Daeumchen.";
+ out_oth="@@gname@@ dreht@@adverb@@ Daeumchen.";
+ return FeelIt();
+
+ /**************** Danken ***************/
+ case "dank":
+ HELPCHECK("dank");
+ ParseRest(str);
+ if (!who)
+ Return("Bei wem willst Du Dich bedanken?\n");
+ if (CheckLife(NOT_SELF, NOT_DEAD,
+ "Leidest Du jetzt schon an Persoenlickeitsspaltung? Ne, ne...",
+ "Keine Reaktion. Ist wohl befriedigender, sich bei Lebewesen zu "
+ +"bedanken."))
+ return 1;
+ out_sel="Du bedankst Dich@@adverb@@ bei@@ wem@@.";
+ out_vic="@@gname@@ bedankt sich@@adverb@@ bei Dir.";
+ out_oth="@@gname@@ bedankt sich@@adverb@@ bei@@ wem@@.";
+ return FeelIt();
+
+ /**************** Denken ***************/
+ case "denk":
+ HELPCHECK("denk");
+ if (ghost())
+ Return("Womit willst Du denn denken? Du hast keine grauen Zellen...\n");
+// ParseRest(str);
+ str2=old_explode(str||""," ")[0];
+ if (str
+ && (!adverb||((QueryStdAdverbs()[str2]||plr_adverbs[str2]))!=adverb)) {
+ out_sel="Du denkst . o O ("+str+")";
+ out_oth="@@name@@ denkt . o O ("+str+")";
+ out_vic="@@name@@ denkt . o O ("+str+")";
+ }
+ else {
+ out_sel="Du faengst@@adverb@@ an zu denken.\nKleine "
+ +"Rauchwoelkchen steigen auf...";
+ out_oth="@@name@@ faengt@@adverb@@ an zu denken.\nKleine "
+ +"Rauchwoelkchen steigen auf...";
+ }
+ return FeelIt();
+
+ /**************** Deuten ***************/
+ case "deut":
+ HELPCHECK("deut");
+ ParseRest(str,"auf");
+ if (for_all) {
+ out_sel="Du deutest@@adverb@@ auf @@alle@@.";
+ out_vic="@@gname@@ deutet@@adverb@@ auf @@alle@@.";
+ return MixedOut(WEN);
+ }
+ if (!who)
+ Return("Auf wen oder was willst Du deuten?\n");
+ out_sel="Du deutest@@adverb@@ auf"
+ +(who==this_object()?" Dich." : "@@ wen@@.");
+ if (who != this_object()) out_vic="@@gname@@ deutet@@adverb@@ auf Dich.";
+ out_oth="@@gname@@ deutet@@adverb@@ auf"
+ +(who==this_object() ? " sich selber.": "@@ wen@@.");
+ return FeelIt();
+
+ /**************** Druecken ***************/
+ case "drueck":
+ HELPCHECK("drueck");
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du drueckst @@alle@@"+(adverb ? "@@adverb@@" : " zaertlich")
+ +" an Dich.";
+ out_vic="@@gname@@ drueckt @@alle@@"+
+ (adverb ? "@@adverb@@" : " zaertlich")+" an sich.";
+ return MixedOut(WEN);
+ }
+ if (!who)
+ Return("Wen willst Du denn druecken?\n");
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Das macht doch keinen Spass.",
+ "Ich druecke nur jemanden, nicht etwas."))
+ return 1;
+ GHOSTCHECK("Du willst "+who->name(WEN)+" an Dich druecken - nur hast Du "
+ +"schon\nwieder nicht daran gedacht, dass so was als Geist nicht "
+ +"geht.\n",
+ gname()+" will "+who->name(WEN)+" an sich druecken - hat aber\n"
+ +"mal wieder nicht an die Nachteile des Geisterdaseins gedacht.\n",
+ gname()+" will Dich an sich druecken - hat aber mal wieder\n"
+ +"nicht an die Nachteile des Geisterdaseins gedacht.\n");
+ out_sel="Du drueckst @@wen@@"+(adverb ? "@@adverb@@" : " zaertlich")
+ +" an Dich.";
+ out_vic="@@name@@ drueckt Dich"+(adverb ? "@@adverb@@" : " zaertlich")
+ +" an sich.";
+ out_oth="@@name@@ drueckt @@wen@@"+(adverb ? "@@adverb@@" : " zaertlich")
+ +" an sich.";
+ return FeelIt();
+
+ /**************** Entschuldige ***************/
+ case "entschuldig":
+ HELPCHECK("entschuldig");
+ ParseRest(str);
+ if (str && !who && !adverb)
+ Return("Entschuldige Dich wie oder bei wem?\n");
+ out_sel="Du bittest"+(who ? " @@wen@@" : "")
+ +"@@adverb@@ um Entschuldigung.";
+ if (who) out_vic="@@gname@@ bittet Dich@@adverb@@ um Entschuldigung.";
+ out_oth="@@gname@@ bittet"+(who ? " @@wen@@" : "")
+ +"@@adverb@@ um Entschuldigung.";
+ return FeelIt();
+
+ /**************** Erbleichen ***************/
+ case "erbleich":
+ HELPCHECK("erbleich");
+ GHOSTCHECK("Ich weiss zwar nicht, wie Du das schaffst, aber Du wirst "
+ +"noch bleicher.\n",
+ break_string("Wie unwahrscheinlich das auch ist, aber "+gname()
+ +" schafft es tatsaechlich, noch bleicher zu werden.",78), 0 );
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Wie willst Du erbleichen?\n");
+ out_sel="Du erbleichst@@adverb@@.";
+ out_oth="@@name@@ erbleicht@@adverb@@.";
+ return FeelIt();
+
+ /**************** Erroeten ***************/
+ case "erroet":
+ HELPCHECK("erroet");
+ GHOSTCHECK("Du schaffst es nur bis zu einem blassen Rosa, aber immerhin.\n",
+ "Die Wangen des Geistes von "+capname()+" werden leicht rosa.\n", 0);
+ ParseRest(str);
+ if (!adverb && str)
+ Return("Erroete wie?\n");
+ out_sel="Deine Wangen gluehen@@adverb@@.";
+ out_oth="@@name@@ erroetet@@adverb@@.";
+ return FeelIt();
+
+ /**************** Erschrecken ***************/
+ case "erschreck":
+ case "erschrick":
+ if (!ghost())
+ Return("Du bist zu harmlos, Geist muesste man sein...\n");
+ HELPCHECK("erschreck");
+ ParseRest(str);
+ if (!who)
+ Return("Wen willst Du denn erschrecken?\n");
+ out_sel="Mit einem lauten BUH! erschreckst Du @@wen@@"
+ +(adverb ? "@@adverb@@." : " fuerchterlich.");
+ out_vic="BUH! Du zuckst vor Schreck zusammen. Muss dieser Geist von "
+ +"@@gname@@ Dich auch@@adverb@@ erschrecken.";
+ out_oth="BUH! @@gname@@ erschreckt @@wen@@"
+ +(adverb ? "@@adverb@@." : " fuerchterlich.");
+ return FeelIt();
+
+ /**************** Flippen ***************/
+ case "flipp":
+ HELPCHECK("flipp");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Ausflippen wollen wir also, so so. Und wie, wenn ich "
+ +"fragen darf?\n");
+ out_sel="Du flippst"+(adverb ? "@@adverb@@ aus." : " total aus.");
+ out_oth="@@gname@@ flippt"+(adverb ? "@@adverb@@ aus." : " total aus.");
+ return FeelIt();
+
+ /**************** Fluchen ***************/
+ case "fluch":
+ HELPCHECK("fluch");
+ GHOSTCHECK("Du faengst mangels Resonanzkoerper leise an zu fluchen.\n",
+ gname()+" faengt leise an zu fluchen. Laut kann er nicht,\n"
+ +"mangels Luft und Resonanzkoerper.\n", 0);
+ if (!str) {
+ out_sel="Du fluchst lautstark.";
+ out_oth="@@name@@ faengt an, fuerchterlich zu fluchen.";
+ }
+ else {
+ ParseRest(str);
+ if (!adverb)
+ Return("Wie willst Du fluchen?\n");
+ out_sel="Du fluchst@@adverb@@.";
+ out_oth="@@name@@ flucht auf einmal@@adverb@@.";
+ }
+ return FeelIt();
+
+ /**************** Fragen ***************/
+ case "frag":
+ HELPCHECK("frag");
+ if (!str)
+ Return("Frage wen was?\n");
+ ParseRest(str);
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Du faengst zu gruebeln an...",
+ "Frage jemand, der lebt."))
+ return 1;
+ if (who)
+ sscanf(str,"%s %s", str1,str1);
+ else
+ str1=str;
+ if (!str1)
+ Return("Frage "+who->name(WEN)+" WAS?\n");
+ str1=capitalize(str1);
+ if (str1[<1] != '?')
+ str1 += "?";
+ out_sel="Du fragst@@ wen@@: "+str1;
+ /* Sprachfluch beruecksichtigen -Wargon, 8. 9. 95 */
+ if (objectp(QueryProp(P_PERM_STRING)))
+ str1 = call_other(QueryProp(P_PERM_STRING), "permutate_string", str1)||"";
+ if (who) out_vic=(ghost() ? "Der Geist von " : /* IS_LEARNER(ME) ?
+ QueryProp(P_PRESAY)||"" : */ "")+capname()+" fragt Dich: "+str1;
+ out_oth=(ghost() ? "Der Geist von " : /* IS_LEARNER(ME) ?
+ QueryProp(P_PRESAY)||"" : */ "")+capname()+" fragt@@ wen@@: "+str1;
+ return FeelIt();
+
+ /**************** Freuen ***************/
+ case "freu":
+ HELPCHECK("freu");
+ ParseRest(str);
+ if (str && !who && !adverb)
+ Return("Freue Dich wie?\n");
+ out_sel="Du "+(who ? "grinst @@wen@@ an und " : "")
+ +"freust Dich@@adverb@@.";
+ if (who) out_vic="@@gname@@ grinst Dich an und freut sich@@adverb@@.";
+ out_oth="@@gname@@ "+(who ? "grinst @@wen@@ an und " : "")
+ +"freut sich@@adverb@@.";
+ return FeelIt();
+
+ /**************** Furzen ***************/
+ case "furz":
+ HELPCHECK("furz");
+ GHOSTCHECK("Du laesst einen fahren - aber er riecht nach gar nix.\n",
+ gname()+" laesst einen fahren. Man riecht aber nix.\n", 0);
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Furze wie?\n");
+ out_sel="Du furzt"+(adverb ? "@@adverb@@." : " hemmungslos.");
+ out_oth="@@name@@ laesst@@adverb@@ einen Stinkefurz fahren.";
+ ofoo=clone_object("/items/furz");
+ ofoo->set_furzer(this_player());
+ ofoo->move(environment(this_player()));
+ return FeelIt();
+// DEBUG Furz testen!
+
+ /**************** Gaehnen ***************/
+ case "gaehn":
+ HELPCHECK("gaehn");
+ if (ghost())
+ Return("Als Geist wirst Du nicht muede - also nicht gaehnen.\n");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Wie willst Du gaehnen?\n");
+ if (!adverb)
+ out_sel="Kannst Du aber Dein(en) Mund/Maul/Schnabel weit aufreissen!";
+ else
+ out_sel="Du gaehnst@@adverb@@.";
+ out_oth="@@gname@@ gaehnt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Glucksen ***************/
+ case "glucks":
+ HELPCHECK("glucks");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Gluckse wie?\n");
+ out_sel="Du gluckst"+(adverb ? "@@adverb@@." : " wie ein Huhn.");
+ out_oth="@@gname@@ gluckst"+(adverb ? "@@adverb@@." : " wie ein Huhn.");
+ return FeelIt();
+
+ /**************** Gratulieren ***************/
+ case "gratulier":
+ case "beglueckwuensch":
+ HELPCHECK("gratulier");
+ ParseRest(str);
+ if (!who)
+ Return("Wem willst Du gratulieren?\n");
+ if (CheckLife(NOT_SELF, NOT_DEAD,
+ "Na, meinst Du nicht, dass Eigenlob stinkt?",
+ "Soll ich dem Ding vielleicht zum Totsein gratulieren? Nee nee."))
+ return 1;
+ out_sel="Du gratulierst @@wem@@@@adverb@@.";
+ out_vic="@@gname@@ gratuliert Dir@@adverb@@.";
+ out_oth="@@gname@@ gratuliert @@wem@@@@adverb@@.";
+ return FeelIt();
+
+ /**************** Grinsen ***************/
+ case "grins":
+ HELPCHECK("grins");
+ GHOSTCHECK("Als Du grinst, siehst Du regelrecht, wie die anderen eine "
+ +"Gaensehaut bekommen.\n",
+ "Du bekommst eine Gaensehaut, als der Geist von "+capname()
+ +" zu grinsen anfaengt.\n", 0);
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du grinst @@alle@@@@adverb@@ an.";
+ out_vic="@@name@@ grinst @@alle@@@@adverb@@ an.";
+ return MixedOut(WEN);
+ }
+ if (!who && !adverb && str)
+ Return("Grinsen - schoen und gut. Aber wen oder wie (an)grinsen?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Du grinst Dir was, aber so, dass es kein anderer sieht.",
+ "Nicht mal einen Spiegel darf man hier angrinsen, nur Lebewesen!"))
+ return 1;
+ out_sel="Du grinst@@ wen@@@@adverb@@"+(who ? " an" : "")+".";
+ if (who) out_vic="@@name@@ grinst Dich@@adverb@@ an.";
+ out_oth="@@name@@ grinst@@ wen@@@@adverb@@"+(who ? " an" : "")+".";
+ return FeelIt();
+
+ /**************** Gruebeln ***************/
+ case "gruebel":
+ case "gruebl":
+ HELPCHECK("gruebel");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Grueble wie?\n");
+ out_sel="Du gruebelst@@adverb@@ eine Weile vor Dich hin.";
+ out_oth="@@gname@@ gruebelt@@adverb@@ eine Weile vor sich hin.";
+ return FeelIt();
+
+ /**************** Grummeln ***************/
+ case "grummel":
+ case "grumml":
+ HELPCHECK("grummel");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Grummle wie?\n");
+ out_sel="Du grummelst@@adverb@@.";
+ out_oth="@@gname@@ grummelt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Grunzen ***************/
+ case "grunz":
+ HELPCHECK("grunz");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Grunze wie?\n");
+ out_sel="Du grunzt@@adverb@@.";
+ out_oth="@@gname@@ grunzt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Gucken ***************/
+ case "guck":
+ HELPCHECK("guck");
+ ParseRest(str);
+ if (!adverb)
+ Return("Gucke wie aus der Waesche?\n");
+ out_sel="Du guckst@@adverb@@ aus der Waesche.";
+ out_oth="@@gname@@ guckt@@adverb@@ aus der Waesche.";
+ return FeelIt();
+
+ /**************** Jammern ***************/
+ case "jammer":
+ HELPCHECK("jammer");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Wie willst Du jammern?\n");
+ out_sel="Du jammerst@@adverb@@.";
+ out_oth="@@gname@@ jammert@@adverb@@.";
+ return FeelIt();
+
+ /**************** Haetscheln ***************/
+ case "haetschel":
+ case "haetschl":
+ HELPCHECK("haetschel");
+ GHOSTCHECK("Du ueberlegst es Dir anders - mit Deinen durchlaessigen "
+ +"Haenden...",
+ gname()+" will anscheinend jemand haetscheln, ueberlegt\n"
+ +"es sich nach einem kurzen Blick auf seine Haende anders.\n", 0);
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du haetschelst @@alle@@@@adverb@@.";
+ out_vic="@@name@@ haetschelt @@alle@@@@adverb@@.";
+ return MixedOut(WEN);
+ }
+ if (!str || !who)
+ Return("Wen willst Du haetscheln?\n");
+ if (who && CheckLife(NOT_SELF, NOT_DEAD,
+ "Das sieht viel zu albern aus - Du laesst es bleiben.",
+ "Ist da ueberhaupt was zu haetscheln? Nein, da lebt doch nix."))
+ return 1;
+ out_sel="Du haetschelst@@ wen@@@@adverb@@.";
+ out_vic="@@name@@ haetschelt Dich@@adverb@@.";
+ out_oth="@@name@@ haetschelt@@ wen@@@@adverb@@.";
+ return FeelIt();
+
+ /**************** Hicksen ***************/
+ case "hicks":
+ HELPCHECK("hicks");
+ GHOSTCHECK("Hoppla! Dieser Hickser zieht Dich ganz schoen zusammen!\n",
+ gname()+" hat anscheinend Schluckauf.\n"
+ +"Und was fuer einen! Fuer einen Moment zieht es "+QueryPronoun(WEN)
+ +" ziemlich zusammen.\n", 0);
+ if (!str) {
+ out_sel="Hicks!";
+ out_oth="@@name@@ muss hicksen. Wahrscheinlich zu viel Alkohol...";
+ }
+ else {
+ ParseRest(str);
+ if (!adverb)
+ Return("Hickse wie?\n");
+ out_sel="Du hickst@@adverb@@.";
+ out_oth="@@name@@ hickst@@adverb@@.";
+ }
+ return FeelIt();
+
+ /**************** Huepfen ***************/
+ case "huepf":
+ HELPCHECK("huepf");
+ GHOSTCHECK("Du schwebst durch die Gegend.\n",
+ gname()+" schwebt durch die Gegend.\n", 0);
+ if (!str) {
+ out_sel="B O I N G !! Du huepfst in der Gegend herum.";
+ out_oth="@@name@@ huepft in der Gegend herum.";
+ }
+ else {
+ ParseRest(str);
+ if (!who && !adverb)
+ Return("Huepfe wie oder um wen oder wie oder was oder haeh?\n");
+ out_sel="Du huepfst@@adverb@@"+(who ? " um@@ wen@@" : "")+" herum.";
+ if (who) out_vic="@@name@@ huepft@@adverb@@ um Dich herum.";
+ out_oth="@@name@@ huepft@@adverb@@"+(who ? " um@@ wen@@" : "")+" herum.";
+ }
+ return FeelIt();
+
+ /**************** Husten ***************/
+ case "hust":
+ HELPCHECK("hust");
+ GHOSTCHECK("Du verstreust ein paar Geisterbazillen im Raum.\n",
+ gname()+" macht ufff, ufff und verteilt ein paar Geister-\n"
+ +"bazillen im Raum.\n", 0);
+ if (!str) {
+ out_sel="Hust! Keuch! Halt dir doch wenigstens die Hand vor den Mund!";
+ out_oth="@@name@@ hustet sich fast die Seele aus dem Leib.";
+ }
+ else {
+ ParseRest(str);
+ if (!who && !adverb)
+ Return("Wenn Du schon was hinter huste tippst, dann bitte was "
+ +"vernuenftiges!\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Dir selber koennen nur andere was husten.",
+ "Bitte huste nur Lebewesen was."))
+ return 1;
+ out_sel="Du hustest@@ wem@@@@adverb@@"+(who? " was" : "")+".";
+ if (who) out_vic="@@name@@ hustet Dir@@adverb@@was.";
+ out_oth="@@name@@ hustet@@ wem@@@@adverb@@"+(who? " was" : "")+".";
+ }
+ return FeelIt();
+
+ /**************** Jubeln ***************/
+ case "jubel":
+ case "jubl":
+ HELPCHECK("jubel");
+ ParseRest(str);
+ if (str && !who && !adverb)
+ Return("Juble wie? Oder wem zu?\n");
+ out_sel="Du jubelst@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+ if (who) out_vic="@@gname@@ jubelt Dir@@adverb@@ zu.";
+ out_oth="@@gname@@ jubelt@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+ return FeelIt();
+
+ /**************** Keuchen ***************/
+ case "keuch":
+ HELPCHECK("keuch");
+ if (ghost())
+ Return("Als Geist strengt Dich nix an - also wird auch nicht "
+ +"gekeucht.\n");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Keuche wie?\n");
+ out_sel="Du keuchst"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+ out_oth="@@name@@ keucht"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+ return FeelIt();
+
+ /**************** Kichern ***************/
+ case "kicher":
+ HELPCHECK("kicher");
+ if (!str) {
+ out_sel="Du kicherst. (Wie albern von Dir)";
+ out_oth="@@gname@@ gibt ein albernes Kichern von sich.";
+ }
+ else {
+ ParseRest(str);
+ if (!who && !adverb)
+ Return("Das haut so nicht hin, gib vernuenftige Parameter.\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "In diesem Fall nimm bitte nur kicher.",
+ "Musst schon etwas Lebendes angeben, nichts Totes."))
+ return 1;
+ out_sel="Du kicherst@@adverb@@"+(who ? " hinter "+who->name(WESSEN)+
+ " Ruecken." : ".");
+ if (who) out_vic="Jemand kichert@@adverb@@ hinter deinem Ruecken.";
+ out_oth="@@gname@@ kichert@@adverb@@"+(who ? " hinter "+who->name(WESSEN)
+ +" Ruecken." : ".");
+ }
+ return FeelIt();
+
+ /**************** Kitzeln ***************/
+ case "kitzel":
+ case "kitzl":
+ HELPCHECK("kitzel");
+ GHOSTCHECK("Mit Deinen immateriellen Fingern schaffst Du das nicht.\n",
+ gname()+" muss gerade feststellen, dass man mit\n"
+ +"immateriellen Fingern nicht kitzeln kann.\n", 0);
+ ParseRest(str);
+ if (!who)
+ Return("Wen willst Du kitzeln?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Du bist doch kein Masochist! Du laesst es bleiben.",
+ "Dinge sind so selten kitzlig. Lass es bleiben."))
+ return 1;
+ if (member(({"highlander","hobo"}), who->query_real_name())>-1)
+ switch (who->query_real_name()) {
+ case "highlander": str1="unter"; str2="Federn"; break;
+ case "hobo" : str1="an"; str2="Kinn"; break;
+ }
+ else if (who->QueryProp(P_RACE))
+ switch (lower_case(who->QueryProp(P_RACE))) {
+ case "drache" : str1="unter";str2="Schuppen";
+ t_g=FEMALE; t_n=PLURAL; break;
+ case "greif" : str1="unter";str2="Federn";
+ t_g=FEMALE; t_n=PLURAL; break;
+ default : str1="an"; str2="Kinn"; t_g=NEUTER; t_n=SINGULAR;
+ }
+ else {
+ str1="an"; str2="Kinn"; t_g=NEUTER; t_n=SINGULAR;
+ }
+ if (getuid(who)=="trest" || getuid(who)=="woelkchen") {
+ str1="an"; str2="Fuessen"; t_g=MALE; t_n=PLURAL;
+ }
+ out_sel="Du kitzelst@@ wen@@@@adverb@@ "+str1+" "+who->QPP(t_g,WEM,t_n)
+ +" "+str2+".\n@@wer@@ versucht, sich zu beherrschen, muss aber "
+ +"doch lachen.";
+ out_vic="@@name@@ kitzelt Dich@@adverb@@ "+str1+" Deine"
+ +(t_n ? "n" : (t_g==FEMALE ? "r" : "m"))+" "+str2
+ +".\nDu versuchst, Dich zu beherrschen, musst aber doch lachen.";
+ out_oth="@@name@@ kitzelt@@ wen@@@@adverb@@ "+str1+" "
+ +who->QPP(t_g,WEM,t_n)+" "+str2
+ +".\n@@wer@@ versucht, sich zu beherrschen, muss aber doch lachen.";
+ return FeelIt();
+
+ /**************** Klatschen ***************/
+ case "klatsch":
+ HELPCHECK("klatsch");
+ GHOSTCHECK("Deine Haende sausen durcheinander durch.\n",
+ gname()+" will in die Haende klatschen - aber sie\n"
+ +"sausen durcheinander durch.\n", 0);
+ ParseRest(str);
+ if (!adverb && str)
+ Return("Klatsche wie?\n");
+ out_sel="Du klatschst@@adverb@@ in die Haende.";
+ out_oth="@@name@@ klatscht@@adverb@@ in die Haende.";
+ return FeelIt();
+
+ /**************** Klopfen ***************/
+ case "klopf":
+ HELPCHECK("klopf");
+ if (!str||sscanf(str,"%s auf schulter",sfoo)!=1)
+ if (!str||sscanf(str,"%s auf die schulter",sfoo)!=1)
+ Return("Klopfe wie wem wieso was?\n");
+ if (ghost())
+ Return("Das geht leider nicht mit durchlaessigen Haenden.\n");
+ str=sfoo;
+ if (str=="") str=0;
+ ParseRest(str);
+ if (!who)
+ Return("Klopfe wem auf die Schulter?\n");
+ out_sel="Du klopfst @@wem@@@@adverb@@ auf die Schulter.";
+ out_vic="@@name@@ klopft Dir@@adverb@@ auf die Schulter.";
+ out_oth="@@name@@ klopft @@wem@@@@adverb@@ auf die Schulter.";
+ return FeelIt();
+
+ /**************** Knabbern ***************/
+ case "knabber":
+ HELPCHECK("knabber");
+ if (ghost())
+ Return("Sorry, aber dafuer fehlt Dir im Moment der noetige "
+ +"\"Biss\"...\n");
+ ParseRest(str);
+ if (!who)
+ Return("Knabbere wen an?\n");
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Du kommst nicht an dein eigenes Ohr ran...",
+ "Noe, noe, das schmeckt bestimmt nicht gut."))
+ return 1;
+ out_sel="Du knabberst@@adverb@@ an "+who->name(WESSEN)+" Ohr.";
+ out_vic="@@name@@ knabbert@@adverb@@ an Deinem Ohr.";
+ out_oth="@@name@@ knabbert@@adverb@@ an "+who->name(WESSEN)+" Ohr.";
+ return FeelIt();
+
+ /**************** Knicksen ***************/
+ case "knicks":
+ HELPCHECK("knicks");
+ GHOSTCHECK("Du knickst in der Mitte ab, kriegst Dich aber schnell wieder "
+ +"zusammen.\n",
+ gname()+" knick(s)t in der Mitte ab, kriegt sich aber\n"
+ +"zum Glueck schnell wieder zusammen.\n", 0);
+ if (!str) {
+ out_sel="Du machst einen anmutigen Knicks.";
+ out_oth="@@name@@ macht einen anmutigen Knicks.";
+ }
+ else {
+ ParseRest(str,"vor");
+ if (for_all) {
+ out_sel="Du knickst@@adverb@@ vor @@alle@@.";
+ out_vic="@@name@@ knickst@@adverb@@ vor @@alle@@.";
+ return MixedOut(WEM);
+ }
+ if (!who && !adverb)
+ Return("Knickse irgendwie oder vor jemandem.\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Wie willst Du das denn schaffen?",
+ "Vor Sachen wird hier nicht geknickst!"))
+ return 1;
+ out_sel="Du knickst@@adverb@@"+(who ? " vor" : "")+"@@ wem@@.";
+ if (who ) out_vic="@@name@@ knickst@@adverb@@ vor Dir.";
+ out_oth="@@name@@ knickst@@adverb@@"+(who ? " vor" : "")+"@@ wem@@.";
+ }
+ return FeelIt();
+
+ /**************** Knirschen ***************/
+ case "knirsch":
+ HELPCHECK("knirsch");
+ if (ghost())
+ Return("Du kannst mit nichts knirschen, so als Geist. Versuche doch "
+ +"dafuer\nmal, zu rasseln...\n");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Knirsche wie?\n");
+ switch (QueryProp(P_RACE)) {
+ case "greif" : str1="dem Schnabel."; break;
+ case "sandtiger" : str1="den Fangzaehnen."; break;
+ case "drache" : str1="den Fangzaehnen."; break;
+ default : str1="den Zaehnen.";
+ }
+ out_sel="Du knirschst@@adverb@@ mit "+str1;
+ out_oth="@@name@@ knirscht@@adverb@@ mit "+str1;
+ return FeelIt();
+
+ /**************** Knuddeln ***************/
+ case "knuddel":
+ case "knuddl":
+ HELPCHECK("knuddel");
+ if (ghost())
+ Return("Sorry, nicht als Geist.\n");
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du knuddelst @@alle@@@@adverb@@.";
+ out_vic="@@name@@ knuddelt @@alle@@@@adverb@@.";
+ return MixedOut(WEN);
+ }
+ if (!who)
+ Return("Knuddle wen?\n");
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Das bringt doch nix, lass es halt.",
+ "Du kannst soviel ich weiss ausser Lebewesen nur Teddys knuddeln."))
+ return 1;
+ out_sel="Du knuddelst@@ wen@@@@adverb@@.";
+ out_vic="@@name@@ knuddelt Dich@@adverb@@.";
+ out_oth="@@name@@ knuddelt@@ wen@@@@adverb@@.";
+ return FeelIt();
+
+ /**************** Knurren ***************/
+ case "knurr":
+ HELPCHECK("knurr");
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du knurrst @@alle@@@@adverb@@ an.";
+ out_vic="@@gname@@ knurrt @@alle@@@@adverb@@ an.";
+ return MixedOut(WEN);
+ }
+ if (str && !who && !adverb)
+ Return("Wen anknurren oder wie knurren?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Du knurrst in Dich hinein.",
+ "Reagiert nicht. Solltest wohl besser Lebwesen anknurren."))
+ return 1;
+ out_sel="Du knurrst@@ wen@@@@adverb@@"+(who ? " an." : ".");
+ if (who) out_vic="@@gname@@ knurrt Dich@@adverb@@ an.";
+ out_oth="@@gname@@ knurrt@@ wen@@@@adverb@@"+(who ? " an." : ".");
+ return FeelIt();
+
+ /**************** Knutschen ***************/
+ case "knutsch":
+ HELPCHECK("knutsch");
+ if (ghost())
+ Return("Das kannst Du als Geist leider nicht. Irgendwie fehlt Dir "
+ +"dazu das Herz.\n");
+ ParseRest(str);
+ if (!who)
+ Return("Knutsche wen ab?\n");
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Das geht nicht.",
+ "Igitt! Lieber nicht!"))
+ return 1;
+ out_sel="Du gibst @@wem@@@@adverb@@ einen RICHTIGEN Kuss.";
+ out_vic="@@name@@ gibt Dir@@adverb@@ einen tiefen und hingebungsvollen "
+ +"Kuss.\nDu schwebst im 7. Himmel.";
+ out_oth="@@name@@ gibt @@wem@@@@adverb@@ einen tiefen und "
+ +"hingebungsvollen Kuss.";
+ return FeelIt();
+
+ /**************** Kotzen ***************/
+ case "kotz":
+ HELPCHECK("kotz");
+ if (ghost())
+ Return("Ne, das ist eins von den Sachen, die als Geist nicht gehen.\n");
+ ParseRest(str);
+ if (str && !who && !adverb)
+ Return("Kotze wie? Kotze auf wen?\n");
+ if (who && CheckLife(NOT_SELF,0,
+ "Igitt, nein danke."))
+ return 1;
+ if(!str) {
+ out_sel="Du kotzt ueber deine Schuhe.";
+ out_oth="@@name@@ verdreht die Augen und kotzt.";
+ }
+ else {
+ out_sel="Du kotzt@@adverb@@"+(who ? " auf @@wen@@." : ".");
+ if (who) out_vic="@@name@@ kotzt@@adverb@@ auf Dich.";
+ out_oth="@@name@@ kotzt@@adverb@@"+(who ? " auf @@wen@@." : ".");
+ }
+ return FeelIt();
+
+ /**************** Kratzen ***************/
+ case "kratz":
+ HELPCHECK("kratz");
+ ParseRest(str);
+ if (who && (who!=this_player()))
+ Return("Das mach mal schoen nur mit Dir selber.\n");
+ if (str && !adverb)
+ Return("Wie willst Du Dich kratzen?\n");
+ out_sel="Du kratzt dich@@adverb@@ am Kopp.";
+ out_oth="@@gname@@ kratzt sich@@adverb@@ am Kopp.";
+ return FeelIt();
+
+ /**************** Krieche ***************/
+ case "kriech":
+ HELPCHECK("kriech");
+ ParseRest(str);
+ out_sel="Du kriechst"+(who ? " vor @@wem@@" : "")+"@@adverb@@ im Staub.";
+ if (who) out_vic="@@gname@@ kriecht@@adverb@@ vor Dir im Staub.";
+ out_oth="@@gname@@ kriecht"+(who ? " vor @@wem@@" : "")
+ +"@@adverb@@ im Staub.";
+ return FeelIt();
+
+ /**************** Kuessen ***************/
+ case "kuess":
+ HELPCHECK("kuess");
+ if (ghost())
+ Return("Als Geist kannst Du leider niemanden kuessen.\n");
+ ParseRest(str);
+ if (!who)
+ Return("Wen willst Du kuessen?\n");
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Da hast Du aber Schwierigkeiten... Du gibst es schliesslich auf.",
+ "Nix. Absolut nix. Kuesse lieber Lebewesen - die reagieren\n"
+ +"wenigstens (und sei es, dass sie Dich fressen...)."))
+ return 1;
+ out_sel="Du kuesst@@ wen@@@@adverb@@.";
+ out_vic="@@name@@ kuesst Dich@@adverb@@.";
+ out_oth="@@name@@ kuesst@@ wen@@@@adverb@@.";
+ FeelIt();
+ if (who->QueryProp(P_FROG)&&QueryProp(P_LEVEL)>who->QueryProp(P_LEVEL)) {
+ tell_room(environment(this_player()),"PLOPP!\n");
+ write("Huch! Du wirst auf einmal so gruen und klein und kriegst auf\n"
+ +"einmal furchtbar Hunger auf Fliegen und so...\n");
+ who->Message("Auf einmal wird die Welt um Dich wieder so klein, wie sie\n"
+ +" frueher mal war - und vor Dir sitzt ein kleiner gruener Frosch.\n");
+ say(who->name(WER)+" steht auf einmal da und schaut dumm aus der "
+ +"Waesche. Dafuer fehlt\njetzt seltsamerweise "+capname()
+ +". Die Gesamtzahl an kleinen gruenen\nFroeschen im Raum hat sich "
+ +"jedoch nicht geaendert...\n",({who,this_player()}));
+ who->SetProp(P_FROG,0);
+ SetProp(P_FROG,1);
+ }
+ return 1;
+
+ /**************** Kuscheln ***************/
+ case "kuschel":
+ case "kuschl":
+ HELPCHECK("kuschel");
+ GHOSTCHECK("Dazu bist Du als Geist viel zu kalt und gar "
+ +"schroecklich anzusehen.\n",
+ gname()+" scheint Anwandlungen zu haben, sich an jemand "
+ +"ankuscheln zu wollen.\nEntsetzt springen alle zurueck, weil "
+ +"dazu ist er doch zu kalt und schroecklich\nanzusehen.\n", 0);
+ ParseRest(str);
+ if (!who)
+ Return("An wen willst Du Dich ankuscheln?\n");
+ out_sel="Du kuschelst Dich@@adverb@@ an @@wen@@ an.";
+ out_vic="@@name@@ kuschelt sich@@adverb@@ an Dich an.";
+ out_oth="@@name@@ kuschelt sich@@adverb@@ an @@wen@@ an.";
+ return FeelIt();
+
+ /**************** Lachen ***************/
+ case "lach":
+ HELPCHECK("lach");
+ GHOSTCHECK("Du lachst mit hohler Stimme. Den Umstehenden (sind doch welche"
+ +" da, oder?)\nlaeuft es kalt den Ruecken runter.\n",
+ gname()+" lacht mit hohler Stimme.\nDir laeuft es eiskalt den Ruecken"
+ +" runter.\n", 0);
+ if (!str) {
+ out_sel="Du brichst vor Lachen zusammen.";
+ out_oth="@@name@@ bricht vor Lachen zusammen.";
+ }
+ else {
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du lachst@@adverb@@ ueber @@alle@@.";
+ out_vic="@@name@@ lacht@@adverb@@ ueber @@alle@@.";
+ return MixedOut(WEN);
+ }
+ if (!who && !adverb)
+ Return("Lache wie, lache ueber wen?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Lach Dich doch nicht selber aus - das machen schon andere...",
+ "Gelacht wird nur ueber Lebewesen (die koennen sich drueber aergern)."))
+ return 1;
+ out_sel="Du lachst@@adverb@@"+(who?" ueber@@ wen@@":"")+".";
+ if (who) out_vic="@@name@@ lacht@@adverb@@ ueber Dich.";
+ out_oth="@@name@@ lacht@@adverb@@"+(who?" ueber@@ wen@@":"")+".";
+ }
+ return FeelIt();
+
+ /**************** Laecheln ***************/
+ case "laechel":
+ case "laechl":
+ HELPCHECK("laechel");
+ if (ghost()) {
+ write("Du laechelst innerlich.\n");
+ return 1;
+ }
+ if(!str) {
+ out_sel="Du laechelst.";
+ out_oth="@@name@@ laechelt.";
+ }
+ else {
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du laechelst @@alle@@@@adverb@@ an.";
+ out_vic="@@name@@ laechelt @@alle@@@@adverb@@ an.";
+ return MixedOut(WEN);
+ }
+ if (!who && !adverb && str)
+ Return("Wie oder wen?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Musst schon jemand anders anlaecheln.",
+ "Bitte ein Lebewesen anlaecheln."))
+ return 1;
+ out_sel="Du laechelst@@ wen@@@@adverb@@"+(who ? " an." : ".");
+ if (who) out_vic="@@name@@ laechelt Dich@@adverb@@ an.";
+ out_oth="@@name@@ laechelt@@ wen@@@@adverb@@"+(who ? " an." : ".");
+ }
+ return FeelIt();
+
+ /**************** Liebe ***************/
+ /* These lines appear Courtesy of Angus@MorgenGrauen. So long, and thanks */
+ /* for all the fish, errr, text, Angus :) */
+ case "lieb":
+ HELPCHECK("lieb");
+ if (ghost())
+ Return("Auf diese Freuden musst Du als Geist leider verzichten.\n");
+ ParseRest(str);
+ if (!who)
+ Return("Wen hast Du lieb?\n");
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Ja, ich weiss, Du magst Dich, aber das musst Du nicht zur Schau"
+ +"stellen.",
+ "Du entwickelst seltsame Neigungen, finde ich."))
+ return 1;
+ str1=(who->QueryProp(P_GENDER)==FEMALE ? "ihr" : "ihm");
+ /* old version:
+ out_sel="Du fluesterst @@wem@@@@adverb@@ liebevolle Worte ins Ohr.";
+ out_vic=gname()+" fluestert Dir@@adverb@@ liebevolle Worte ins Ohr.";
+ out_oth=gname()+" fluestert@@adverb@@ sanfte Worte zu @@wem@@.";
+ */
+ out_sel="Du schliesst die Augen, schmiegst Dich eng an @@wen@@ und gibst"
+ +LF+str1+" einen zaertlichen und leidenschaftlichen Kuss.\n"
+ +"Um Dich herum versinkt die Welt und Du glaubst, auf Wolken zu "
+ +"schweben.";
+ out_vic="@@name@@ drueckt Dich zaertlich an sich und gibt Dir\n"
+ +"einen zaertlichen und leidenschaftlichen Kuss. Du schliesst die\n"
+ +"Augen und traeumst ein wenig......Du schwebst auf Wolken direkt\n"
+ +"in den siebten Himmel.";
+ out_oth="Du schaust dezent weg, als sich @@name@@ und "+who->name()
+ +" verliebt in die Arme\nsinken.";
+ return FeelIt();
+
+ /**************** Loben ***************/
+ case "lob":
+ HELPCHECK("lob");
+ if (!str)
+ Return("Wen oder was willst Du loben?\n");
+ ParseRest(str);
+ if (who==ME) {
+ ofoo=clone_object("/items/furz");
+ ofoo->set_furzer(this_player());
+ ofoo->set_eigenlob();
+ ofoo->move(environment(this_player()));
+ //DEBUG Furz zum Eigenlob patchen :>
+ out_sel="Du lobst Dich selber@@adverb@@. Die Folgen kennst Du ja...";
+ out_oth="@@gname@@ lobt sich selber@@adverb@@, mit den bekannten Folgen.";
+ } else if (who) {
+ out_sel="Du lobst @@wen@@@@adverb@@.";
+ out_vic="@@gname@@ lobt Dich@@adverb@@.";
+ out_oth="@@gname@@ lobt @@wen@@@@adverb@@.";
+ } else {
+ out_sel="Du lobst "+str+".";
+ out_oth="@@gname@@ lobt "+str+".";
+ }
+ return FeelIt();
+
+ /**************** Moppern ***************/
+ case "mopper":
+ HELPCHECK("mopper");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Moppere wie?\n");
+ out_sel="Du mopperst@@adverb@@.";
+ out_oth="@@gname@@ moppert@@adverb@@.";
+ return FeelIt();
+
+ /**************** Mustern ***************/
+ case "muster":
+ HELPCHECK("muster");
+ ParseRest(str);
+ if (!who)
+ Return("Mustere wen?\n");
+ out_sel="Du musterst @@wen@@@@adverb@@.";
+ out_vic="@@gname@@ mustert Dich@@adverb@@.";
+ out_oth="@@gname@@ mustert @@wen@@@@adverb@@.";
+ return FeelIt();
+
+ /**************** Nicken ***************/
+ case "ja":
+ case "nick":
+ HELPCHECK("nick");
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du nickst @@alle@@@@adverb@@ zu.";
+ out_vic="@@gname@@ nickt @@alle@@@@adverb@@ zu.";
+ return MixedOut(WEM);
+ }
+ if (str && !who && !adverb)
+ Return("Nicke wie oder wem zu oder wem wie zu?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Du willst Dir selber zunicken? Lieber nicht, das sieht so albern aus.",
+ "Hm. Nix passiert. Von Lebewesen bekommt man meistens mehr Feedback."))
+ return 1;
+ out_sel="Du nickst@@ wem@@@@adverb@@"
+ +(who ? " zu." : (adverb ? "." : " zustimmend."));
+ if (who) out_vic="@@gname@@ nickt Dir@@adverb@@ zu.";
+ out_oth="@@gname@@ nickt@@ wem@@@@adverb@@"
+ +(who ? " zu." : (adverb ? "." : " zustimmend."));
+ return FeelIt();
+
+ /**************** Niesen ***************/
+ case "nies":
+ HELPCHECK("nies");
+ if (ghost())
+ Return("Du hast keine Nase mehr, in der es Dich jucken koennte...\n");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Niese wie?\n");
+ out_sel="Haaaaaa-tschi! Gesundheit!"+(adverb ? " Du niest@@adverb@@." : "");
+ out_oth="Haaaaaa-tschi! @@name@@ niest@@adverb@@.";
+ return FeelIt();
+
+ /**************** Ohrfeigen ***************/
+ case "gib":
+ HELPCHECK("gib");
+ if (!str)
+ Return("Gib wem was?\n");
+ if (sscanf( str,"%s ohrfeige",str1)==0)
+ return 0;
+ ParseRest(str, ({"ohrfeige", "eine ohrfeige"}));
+ if (for_all) {
+ out_sel="Du verpasst @@alle@@@@adverb@@ eine Ohrfeige.";
+ out_vic="@@name@@ verpasst @@alle@@@@adverb@@ eine Ohrfeige.";
+ return MixedOut(WEM);
+ }
+ if (!who)
+ Return("Gib wem eine Ohrfeige?\n");
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Dazu sind Dir Deine Backen doch zu schade.",
+ "Du wirst doch nichts schlagen, was sich nicht wehren kann?"))
+ return 1;
+ GHOSTCHECK("Deine Hand saust mit voller Wucht durch dein Opfer durch!",
+ gname()+" will "+who->name(WEM)+" eine Ohrfeige geben - aber die Hand\n"
+ +"saust mit voller Wucht durch das Opfer durch!", 0);
+ out_sel="Du verpasst @@wem@@@@adverb@@ eine schallende Ohrfeige.";
+ out_vic="@@name@@ verpasst Dir@@adverb@@ eine Watsche, dass Dir Hoeren "
+ +"und Sehen vergeht.";
+ out_oth="@@name@@ verpasst @@wem@@@@adverb@@ eine schallende Ohrfeige.";
+ return FeelIt();
+
+ /**************** Pfeifen ***************/
+ case "pfeif":
+ HELPCHECK("pfeif");
+ GHOSTCHECK("Es kommt leider nur (nicht mal heisse) Luft, aber kein "
+ +"Pfiff.\n",
+ gname()+" spitzt den Mund und pustet angestrengt. Nichts passiert.\n", 0);
+ ParseRest(str, "nach");
+ if (str && !who && !adverb)
+ Return("Pfeife wie? Pfeife wem nach? Haeh?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Was willst Du denn damit ausdruecken? Das gibt fuer mich keinen Sinn.",
+ "Ich habe keine Lust dazu."))
+ return 1;
+ out_sel="Du pfeifst@@ wen@@@@adverb@@"
+ +(who ? " nach." : (adverb ? "." : " anerkennend."));
+ if (who) out_vic="@@name@@ pfeift Dir@@adverb@@ nach.";
+ out_oth="@@name@@ pfeift@@ wen@@@@adverb@@"
+ +(who ? " nach." : (adverb ? "." :" anerkennend."));
+ return FeelIt();
+
+ /**************** Philosophieren ***************/
+ case "philosophier":
+ HELPCHECK("philosophier");
+ ParseRest(str);
+ out_sel="Du philosophierst"+(adverb ? "@@adverb@@." :
+ (str ? " ueber "+str+"." : "."));
+ out_oth="@@gname@@ philosophiert"+(adverb ? "@@adverb@@." :
+ (str ? " ueber "+str+"." : "."));
+ return FeelIt();
+
+ /**************** (Nase) Putzen ***************/
+ case "putz":
+ HELPCHECK("putz");
+ if (ghost())
+ Return("Nix da zum Putzen, so nebuloes, wie Du bist.\n");
+ ParseRest(str, ({"nase", "die nase"}));
+ if (str && str!="nase" && !adverb)
+ Return("Putze Deine Nase wie?\n");
+ out_sel="Du putzt Deine Nase@@adverb@@.";
+ out_oth="@@name@@ putzt@@adverb@@ "+QPP(FEMALE,WEN)+" Nase.";
+ return FeelIt();
+
+ /**************** Quaken ***************/
+ case "quak":
+ HELPCHECK("quak");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Quake wie?\n");
+ sfoo="";
+ flag=QueryProp(P_FROG)&&!ghost();
+ for (t_g=0; t_g<=random(flag ? 4 : 2); t_g++) {
+ sfoo+=(flag ? " Qu" : " kw");
+ for (t_n=0; t_n<=random(flag ? 10 : 5); t_n++)
+ sfoo+="aA"[random(1)..random(1)];
+ sfoo+="k";
+ }
+ if (!flag)
+ sfoo=lower_case(sfoo);
+ else
+ sfoo+="!";
+ out_sel="Du quakst"+(adverb ? "@@adverb@@" : (flag ? " aus voller Kehle"
+ : " in etwa wie ein Frosch"))+":"+sfoo;
+ out_oth="@@gname@@ quakt"+(adverb ? "@@adverb@@" : (flag ? " aus voller Kehle"
+ : " in etwa wie ein Frosch"))+":"+sfoo;
+ return FeelIt();
+
+ /**************** Quietschen ***************/
+ case "quietsch":
+ case "quiek":
+ HELPCHECK("quiek");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Quietsche wie?\n");
+ out_sel="Du quietschst"+(adverb ? "@@adverb@@." : " vergnuegt.");
+ out_oth="@@gname@@ quietscht"+(adverb ? "@@adverb@@." : " vergnuegt.");
+ return FeelIt();
+
+ /**************** Raeuspern ***************/
+ case "raeusper":
+ HELPCHECK("raeusper");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Hm? Wie meinen?\n");
+ out_sel="Du raeusperst Dich@@adverb@@.";
+ out_oth="@@gname@@ raeuspert sich@@adverb@@.";
+ return FeelIt();
+
+ /**************** Reiben ***************/
+ case "reib":
+ HELPCHECK("reib");
+ if (ghost())
+ Return("Du hast nichts zum Reiben, aber auch gar nichts.\n");
+ if (str && (sscanf(str,"%s die Augen",sfoo)==1 || sscanf(str,"%s Augen",sfoo)==1))
+ str=sfoo;
+ else if (str && (sscanf(str,"%s die Haende",sfoo)==1
+ ||sscanf(str,"%s Haende",sfoo)==1)) {
+ flag=2;
+ str=sfoo;
+ }
+ if (str=="") str=0;
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Reibe wie die "+(flag==2 ? "Haende" : "Augen")+"?\n");
+ out_sel="Du reibst Dir"+(adverb ? "@@adverb@@"
+ : (flag==2 ? " vergnuegt" : " muede"))+" die "
+ +(flag==2 ? "Haende." : "Augen.");
+ out_oth="@@name@@ reibt sich"+(adverb ? "@@adverb@@"
+ : (flag==2 ? " vergnuegt" : " muede"))+" die "
+ +(flag==2 ? "Haende." : "Augen.");
+ return FeelIt();
+
+ /**************** Rotfln ***************/
+ case "rotfl":
+ HELPCHECK("rotfl");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Rotfl wie?\n");
+ out_sel="Du rotflst@@adverb@@.";
+ out_oth="@@gname@@ rotflt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Ruelpsen ***************/
+ case "ruelps":
+ HELPCHECK("ruelps");
+ GHOSTCHECK("Ein leichter Windhauch entfaehrt Deinem Mund, mehr nicht.\n",
+ "Dem Mund des Geistes von "+capname()
+ +" entfaehrt ein leichtes Lueftchen.\n", 0);
+ if (!str) {
+ out_sel="BOOOOEEERRRRPP! Entschuldige dich gefaelligst!";
+ out_oth="@@name@@ ruelpst unanstaendig.";
+ }
+ else {
+ ParseRest(str);
+ if (!adverb) {
+ write("Ruelpse wie (schlimm genug, dass Du Dich nicht beherrschen "
+ +"kannst!)?\n");
+ return 1;
+ }
+ out_sel="Du ruelpst@@adverb@@. Schaem Dich!";
+ out_oth="@@name@@ ruelpst@@adverb@@ und wird nicht mal rot dabei.";
+ }
+ return FeelIt();
+ }
+
+ switch (vb) {
+ /**************** Runzeln ***************/
+ case "runzel":
+ case "runzl":
+ HELPCHECK("runzel");
+ if (ghost())
+ Return("DAS geht als Geist nun wirklich nicht.\n");
+ ParseRest(str,"stirn");
+ if (str && !adverb)
+ Return("Runzle die Stirn wie?\n");
+ out_sel="Du runzelst@@adverb@@ die Stirn.";
+ out_oth="@@name@@ runzelt@@adverb@@ die Stirn.";
+ return FeelIt();
+
+ /**************** Sabbere ***************/
+ case "sabber":
+ HELPCHECK("sabber");
+ sfoo=ghost() ? "schleim" : "sabber";
+ ParseRest(str);
+ if (str && !adverb && !who)
+ Return("Sabber wie oder wen an?\n");
+ out_sel="Du "+sfoo+"st@@ wen@@@@adverb@@ "
+ +(who ? "an." : "auf den Boden.");
+ if (who) out_vic="@@gname@@ "+sfoo+"t Dich@@adverb@@ an.";
+ out_oth="@@gname@@ "+sfoo+"t@@ wen@@@@adverb@@ "
+ +(who ? "an." : "auf den Boden.");
+ return FeelIt();
+
+ /**************** Schaemen ***************/
+ case "schaem":
+ HELPCHECK("schaem");
+ ParseRest(str);
+ if (str && !adverb && lower_case(str)!="dich")
+ Return("Schaeme Dich wie?\n");
+ out_sel="Du schaemst Dich@@adverb@@.";
+ out_oth="@@gname@@ schaemt sich@@adverb@@.";
+ return FeelIt();
+
+#ifdef SCHAU_AN
+ /**************** Schau an ***************/
+ case "schau":
+ HELPCHECK("schau");
+ if (!str || old_explode(str, " ")[sizeof(old_explode(str, " "))]!="an")
+ return 0;
+ ParseRest(str, "an");
+ if (!who)
+ Return("Schau wen an?\n");
+ out_sel="Du schaust @@wen@@@@adverb@@ an.";
+ out_vic="@@gname@@ schaut Dich@@adverb@@ an.";
+ out_oth="@@gname@@ schaut @@wen@@@@adverb@@ an.";
+ return FeelIt();
+#endif
+
+ /**************** Schluchzen ***************/
+ case "schluchz":
+ HELPCHECK("schluchz");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Schluchze wie?\n");
+ out_sel="Du schluchzt"+(adverb ? "@@adverb@@." : " herzzerreissend.");
+ out_oth="@@gname@@ schluchzt"
+ +(adverb ? "@@adverb@@." : " herzzerreissend.");
+ return FeelIt();
+
+ /**************** Schmollen ***************/
+ case "schmoll":
+ HELPCHECK("schmoll");
+ GHOSTCHECK("Du schwebst beleidigt in die Ecke.\n",
+ gname()+" schwebt beleidigt in die Ecke und schmollt.\n", 0);
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Schmolle wie?\n");
+ out_sel="Du schmollst@@adverb@@.";
+ out_oth="@@name@@ geht in die Ecke und schmollt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Schmunzeln ***************/
+ case "schmunzel":
+ case "schmunzl":
+ HELPCHECK("schmunzel");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Schmunzle wie?\n");
+ out_sel="Du schmunzelst@@adverb@@.";
+ out_oth="@@gname@@ schmunzelt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Schnalzen ***************/
+ case "schnalz":
+ HELPCHECK("schnalz");
+ ParseRest(str, ({"zunge","mit zunge", "mit der zunge"}));
+ out_sel="Du schnalzt@@adverb@@ mit der Zunge.";
+ out_oth="@@gname@@ schnalzt@@adverb@@ mit der Zunge.";
+ return FeelIt();
+
+ /**************** Schnauben ***************/
+ case "schnaub":
+ HELPCHECK("schnaub");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Schnaube wie?\n");
+ out_sel="Du schnaubst"+(adverb ? "@@adverb@@." : " entruestet.");
+ out_oth="@@gname@@ schnaubt"+(adverb ? "@@adverb@@." : " entruestet.");
+ return FeelIt();
+
+ /**************** Schnaufen ***************/
+ case "schnauf":
+ HELPCHECK("schnauf");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Schnaufe wie?\n");
+ out_sel="Du schnaufst"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+ out_oth="@@gname@@ schnauft"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+ return FeelIt();
+
+ /**************** Schnippen ***************/
+ case "schnipp":
+ case "schnipps":
+ HELPCHECK("schnipp");
+ GHOSTCHECK("Du schaffst es nicht, weil die Finger durcheinander durch "
+ +"gehen.\n",
+ gname()+" versagt beim Schnippen - die Finger\ngehen durcheinander "
+ +"durch.\n", 0);
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Schnippe wie?\n");
+ out_sel="Du schnippst@@adverb@@ mit deinen Fingern.";
+ out_oth="@@name@@ schnippt@@adverb@@ mit den Fingern.";
+ return FeelIt();
+
+ /**************** Schnarchen ***************/
+ case "schnarch":
+ HELPCHECK("schnarch");
+ if (ghost())
+ Return("Ich glaube, da fehlen Dir irgendwie die physischen "
+ +"Voraussetzungen dazu.\n");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Schnarche wie?\n");
+ out_sel=(str ? "Zzzzzzzzzzz..." : "Du schnarchst@@adverb@@.");
+ out_oth="@@name@@ schnarcht "+(str ? "@@adverb@@." : "laut.");
+ return FeelIt();
+
+ /**************** Schniefen ***************/
+ case "snief":
+ case "schnief":
+ HELPCHECK("schnief");
+ GHOSTCHECK("Du schniefst ganz leise.\n",
+ gname()+" schnieft ganz leise.\n", 0);
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Schniefe wie?\n");
+ out_sel="Du schniefst@@adverb@@.";
+ out_oth="@@name@@ schnieft@@adverb@@.";
+ return FeelIt();
+
+ /**************** Schnurren ***************/
+ case "schnurr":
+ HELPCHECK("schnurr");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Wie willst Du schnurren?\n");
+ out_sel="MMMMIIIIIAAAAAAUUUUUUUU! Du schnurrst"
+ +(adverb ? "@@adverb@@." : " zufrieden.");
+ out_oth="@@gname@@ schnurrt"+(adverb ? "@@adverb@@." : " zufrieden.");
+ return FeelIt();
+
+ /**************** Schreien ***************/
+ case "schrei":
+ HELPCHECK("schrei");
+ GHOSTCHECK("AAAAIIIIIIIIIIIEEEEEEEEEEEEEEEEEEEEEEEEEE! Ja, nur Geister "
+ +"koennen so schreien!\n",
+ gname()+" schreit - das Blut gefriert fast in deinen Ader!\n", 0);
+ if (!str) {
+ out_sel="AUUUAAAHHHHHH!!!!";
+ out_oth="@@name@@ schreit laut!";
+ }
+ else {
+ ParseRest(str);
+ if (!who && !adverb)
+ Return("Schreien - wie denn? Oder wen denn anschreien?\n");
+ out_sel="Du schreist@@ wen@@@@adverb@@"+(who ? " an" : "")+".";
+ if (who) out_vic="@@name@@ schreit Dich@@adverb@@ an.";
+ out_oth="@@name@@ schreit@@ wen@@@@adverb@@"+(who? " an" : "")+".";
+ }
+ return FeelIt();
+
+ /**************** Schuetteln ***************/
+ case "schuettel":
+ case "schuettl":
+ HELPCHECK("schuettel");
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du schuettelst @@alle@@@@adverb@@ die Haende.";
+ out_vic="@@gname@@ schuettelt @@alle@@@@adverb@@ die Haende.";
+ return MixedOut(WEM);
+ }
+ if (str && !who && !adverb)
+ Return("Schuettle wie? Schuettle wem die Hand?\n");
+ if(!who) {
+ out_sel="Du schuettelst Dich@@adverb@@.";
+ out_oth="@@gname@@ schuettelt sich@@adverb@@.";
+ }
+ else {
+ if (CheckLife(0,NOT_DEAD,
+ "", "Noe, das mach ich nur mit Lebewesen."))
+ return 1;
+ if (who == this_player()) {
+ out_sel="Du hebst"+(adverb ? "@@adverb@@" : " triumphierend")
+ +" Deine Haende ueber den Kopf und schuettelst sie.";
+ out_oth="@@gname@@ hebt"+(adverb ? "@@adverb@@" : " triumphierend")
+ +" die Haende ueber den Kopf\nund schuettelt sie.";
+ }
+ else {
+ out_sel="Du schuettelst@@ wem@@@@adverb@@ die Haende.";
+ if (ghost()) out_sel+="\nNaja, Du versuchst es wenigstens - "
+ +"immer diese durchlaessigen Haende...";
+ out_vic="@@gname@@ schuettelt Dir@@adverb@@ die Haende.";
+ if (ghost()) out_vic+="\nNaja, Du versuchst es wenigstens - "
+ +"immer diese durchlaessigen Haende...";
+ out_oth="@@gname@@ schuettelt@@ wem@@@@adverb@@ die Haende.";
+ if (ghost()) out_oth+="\nNaja, Du versuchst es wenigstens - "
+ +"immer diese durchlaessigen Haende...";
+ }
+ }
+ return FeelIt();
+
+ /**************** Schweigen ***************/
+ case "schweig":
+ HELPCHECK("schweig");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Schweige wie?\n");
+ out_sel="Du schweigst@@adverb@@.";
+ out_oth="@@gname@@ schweigt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Seufzen ***************/
+ case "seufz":
+ HELPCHECK("seufz");
+ GHOSTCHECK("Du seufzt geisterhaft.\n",
+ gname()+" seufzt geisterhaft. Naja, wie denn sonst?\n", 0);
+ ParseRest(str);
+ if (!adverb && str)
+ Return("Seufze wie?\n");
+ out_sel="Du seufzst@@adverb@@.";
+ out_oth="@@name@@ seufzt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Singen ***************/
+ case "sing":
+ HELPCHECK("sing");
+ if (!str) {
+ out_sel="Oh sole mio!";
+ out_oth="@@gname@@ singt irgendwas italienisches.";
+ }
+ else {
+ ParseRest(str);
+ out_sel="Du singst@@adverb@@"+(adverb ? "." : " '"+capitalize(str)+"'.");
+ out_oth="@@gname@@ singt@@adverb@@"+(adverb ? "." : " '"
+ +capitalize(str)+"'.");
+ }
+ return FeelIt();
+
+ /**************** Sniffen ***************/
+ case "sniff":
+ HELPCHECK("sniff");
+ ParseRest(str);
+ if (str && !adverb && !who)
+ Return("Sniffe wie?\n");
+ out_sel="Du sniffst"+(who ? " @@wen@@" : "")
+ +(adverb ? "@@adverb@@" : " traurig")+(who ? " an." : ".");
+ if (who) out_vic="@@gname@@ snifft Dich"
+ +(adverb ? "@@adverb@@" : " traurig")+" an.";
+ out_oth="@@gname@@ snifft"+(who ? " @@wen@@" : "")
+ +(adverb ? "@@adverb@@" : " traurig")+(who ? " an." : ".");
+ return FeelIt();
+
+ /**************** Spucken ***************/
+ case "spuck":
+ HELPCHECK("spuck");
+ GHOSTCHECK("Du bringst nicht genug Spucke zusammen.\n",
+ gname()+" stellt gerade fest, dass man ohne Spucke nicht\n"
+ +"spucken kann.\n", 0);
+ ParseRest(str);
+ if (str && !who && !adverb)
+ Return("Spucke wen wie an?\n");
+ if (who && CheckLife(NOT_SELF,0,
+ "Hast Du Dich so schlecht benommen? Lass es lieber bleiben."))
+ return 1;
+ out_sel="Du spuckst@@ wen@@@@adverb@@ "+(who ? "an." : "auf den Boden.");
+ if (who) out_vic="@@name@@ spuckt Dich@@adverb@@ an.";
+ out_oth="@@name@@ spuckt@@ wen@@@@adverb@@ "
+ +(who ? "an." : "auf den Boden.");
+ return FeelIt();
+
+ /**************** Stampfen ***************/
+ case "stampf":
+ HELPCHECK("stampf");
+ ParseRest(str, "auf");
+ out_sel="Du stampfst@@adverb@@ mit dem Fuss auf.";
+ out_oth="@@gname@@ stampft@@adverb@@ mit dem Fuss auf.";
+ return FeelIt();
+
+ /**************** Starren ***************/
+ case "starr":
+ HELPCHECK("starr");
+ ParseRest(str);
+ if (str && !who && !adverb)
+ Return("Starre wie bzw. wen an?\n");
+ if (who && CheckLife(NOT_SELF,0,
+ "Wie willst Du in Deine eigenen Augen starren? "
+ +"(Spiegel gelten nicht...)"))
+ return 1;
+ out_sel="Du starrst"+(!str ? " ins Leere." : (who ? "@@ wen@@" : "")
+ +(adverb ? "@@adverb@@" : " vertraeumt")+(who ? " an." : "."));
+ if (who) out_vic="@@gname@@ starrt"+(adverb ? "@@adverb@@" : " tief")
+ +" in Deine Augen.";
+ out_oth="@@gname@@ starrt"+(!str ? " ins Leere." : (who ? "@@ wen@@" : "")
+ +(adverb ? "@@adverb@@" : " vertraeumt")+(who ? " an." : "."));
+ return FeelIt();
+
+ /**************** Staunen ***************/
+ case "staun":
+ HELPCHECK("staun");
+ if (!str) {
+ out_sel="Du bist erstaunt.";
+ out_oth="@@gname@@ ist erstaunt.";
+ }
+ else {
+ ParseRest(str, "ueber");
+ if (!who && !adverb)
+ Return("Bla bla. Wenn Du nach staune noch was tippst, sollte "
+ +"das ein\nLebewesen sein.\n");
+ if (who == this_player()) {
+ out_sel="Du staunst@@adverb@@ ueber Dich selber.";
+ out_oth="@@gname@@ staunt@@adverb@@ ueber sich selber.";
+ }
+ else {
+ out_sel="Du staunst@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+ if (who) out_vic="@@gname@@ staunt@@adverb@@ ueber Dich.";
+ out_oth="@@gname@@ staunt@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+ }
+ }
+ return FeelIt();
+
+ /**************** Stieren ***************/
+ case "stier":
+ HELPCHECK("stier");
+ GHOSTCHECK("Du stierst mit hohlem Blick in die Gegend.\n",
+ gname()+" stiert mit hohlem Blick in die Gegend.\n", 0);
+ ParseRest(str);
+ if (str && !who && !adverb)
+ Return("Stiere wie oder wen an?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Du kannst Dich nicht selber anstieren.",
+ "Bitte nur Lebewesen anstieren."))
+ return 1;
+ out_sel="Du stierst@@ wen@@@@adverb@@"
+ +(who ? " an." : " in der Gegend herum.");
+ if (who) out_vic="@@gname@@ stiert Dich@@adverb@@ an.";
+ out_oth="@@gname@@ stiert@@ wen@@@@adverb@@"
+ +(who ? " an." : " in der Gegend herum.");
+ return FeelIt();
+
+ /**************** Stimme zu ***************/
+ case "stimm":
+ HELPCHECK("stimm");
+ ParseRest(str, "zu");
+ if (str && !who && !adverb)
+ Return("Stimme wem zu?\n");
+ out_sel="Du stimmst@@ wem@@@@adverb@@ zu.";
+ if (who) out_vic="@@gname@@ stimmt Dir@@adverb@@ zu.";
+ out_oth="@@gname@@ stimmt@@ wem@@@@adverb@@ zu.";
+ return FeelIt();
+
+ /**************** Stoehnen ***************/
+ case "stoehn":
+ HELPCHECK("stoehn");
+ GHOSTCHECK("Du stoehnst schauderlich.\n",
+ gname()+" stoehnt schauderlich. Zum Glueck\nhast Du ziemlich "
+ +"gute Nerven.\n", 0);
+ ParseRest(str);
+ if (!adverb && str)
+ Return("Wie willst Du stoehnen?\n");
+ out_sel="Du stoehnst@@adverb@@.";
+ out_oth="@@name@@ stoehnt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Stossen ***************/
+ case "stoss":
+ HELPCHECK("stoss");
+ ParseRest(str);
+ if (!who)
+ Return("Stosse wen?\n");
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Was soll der Unsinn? Lass das!",
+ "Das gibt nur bei Lebewesen Sinn."))
+ return 1;
+ GHOSTCHECK("Dein Ellenbogen versinkt in "+who->name(WEM)+".\n",
+ gname()+" will "+who->name(WEM)+" in die Rippen stossen, aber "
+ +QPP(MALE,WER,PLURAL)+"\nEllenbogen verteilen keinen Stoss, "
+ +"sondern versinken.\n",
+ gname()+" will Dich in die Rippen stossen, aber "+QPP(MALE,WER,PLURAL)
+ +" Ellenbogen versinken.\n");
+ out_sel="Du stoesst@@ wen@@@@adverb@@ in die Rippen.";
+ out_vic="@@name@@ stoesst Dir@@adverb@@ in die Rippen.";
+ out_oth="@@name@@ stoesst@@ wen@@@@adverb@@ in die Rippen.";
+ return FeelIt();
+
+ /**************** Streicheln ***************/
+ case "streichel":
+ case "streichl":
+ HELPCHECK("streichel");
+ ParseRest(str);
+ if (!who)
+ Return("Streichle wen?\n");
+ if (for_all) {
+ out_sel="Du streichelst @@alle@@@@adverb@@.";
+ out_vic="@@gname@@ streichelt @@alle@@@@adverb@@.";
+ return MixedOut(WEN);
+ }
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Lass Dich von anderen streicheln.",
+ "Ich streichle nur Lebewesen."))
+ return 1;
+ GHOSTCHECK("Du willst "+who->name(WEN,2)+" streicheln, aber Deine "
+ +"Haende koennen\nnichts beruehren.\n",
+ gname()+" will "+who->name(WEN,2)+" streicheln, aber diese\n"
+ +"Geisterhaende koennen halt nix beruehren...\n",
+ gname()+" will Dich streicheln, scheitert aber wie so oft an\n"
+ +"diesen dummen durchlaessigen Geisterhaenden.\n");
+ out_sel="Du streichelst @@wen@@@@adverb@@.";
+ out_vic="@@name@@ streichelt Dich@@adverb@@.";
+ out_oth="@@name@@ streichelt @@wen@@@@adverb@@.";
+ return FeelIt();
+
+ /**************** Stupsen ***************/
+ case "stups":
+ HELPCHECK("stups");
+ if (ghost())
+ Return("Das geht nicht ohne Ellenbogen,..\n");
+ ParseRest(str);
+ if (!who)
+ Return("Stupse wen an?\n");
+ out_sel="Du stupst @@wen@@@@adverb@@ an.";
+ out_vic="@@name@@ stupst Dich@@adverb@@ an.";
+ out_oth="@@name@@ stupst @@wen@@@@adverb@@ an.";
+ return FeelIt();
+
+ /**************** Stutzen ***************/
+ case "stutz":
+ HELPCHECK("stutz");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Stutze wie?\n");
+ out_sel="Du stutzt@@adverb@@.";
+ out_oth="@@gname@@ stutzt@@adverb@@.";
+ return FeelIt();
+
+ /**************** Taetscheln ***************/
+ case "taetschel":
+ case "taetschl":
+ HELPCHECK("taetschel");
+ ParseRest(str);
+ if (!who)
+ Return("Taetschle wen?\n");
+ if (CheckLife(NOT_SELF,NOT_DEAD,
+ "Das sieht zu doof aus, das mache ich nicht.",
+ "Ich taetschle nur Lebewesen."))
+ return 1;
+ GHOSTCHECK("Du willst "+who->name(WEN)+" taetscheln - aber Deine "
+ +"Haende gehen\nglatt durch den Kopf durch.\n",
+ gname()+" will "+who->name(WEN)+" den Kopf taetscheln, aber "
+ +"die Geister-\nhaende gehen glatt durch den Kopf durch.\n",
+ gname()+" will Deinen Kopf taetscheln, aber diese Geisterhaende "
+ +"gehen\nglatt durch Deinen Kopf durch - Du hast ein seltsames "
+ +"Gefuehl dabei.\n");
+ out_sel="Du taetschelst @@wem@@@@adverb@@ den Kopf.";
+ out_vic="@@name@@ taetschelt Dir@@adverb@@ den Kopf.";
+ out_oth="@@name@@ taetschelt @@wem@@@@adverb@@ den Kopf.";
+ return FeelIt();
+
+ /**************** Tanzen ***************/
+ case "tanz":
+ HELPCHECK("tanz");
+ GHOSTCHECK("Du tanzt den klassischen GeisterTanz (tm).\n",
+ gname()+" tanzt den klassischen GeisterTanz (tm).\n", 0);
+ if (!str) {
+ out_sel="Kommst Du Dir nicht irgendwie bloed vor? Du tanzt "
+ +"den Ententanz.";
+ out_oth="@@name@@ tanzt den Ententanz.";
+ }
+ else {
+ taenze=({"Walzer","Polka","Rumba","Tango","Cha cha cha","Foxtrott",
+ "Mambo","Salsa","Slowfox","Breakdance","Pogo","Merengue",
+ "Rock'n'Roll","Ballett","Regentanz","Hexentanz"});
+ ParseRest(str,"mit");
+ if (!who)
+ Return("Mit wem willst Du tanzen?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Mit Dir selber kannst Du nicht tanzen.",
+ "Keine Reaktion - will wahrscheinlich nicht tanzen."))
+ return 1;
+ ifoo=random(sizeof(taenze));
+ out_sel="Du tanzt@@adverb@@ mit @@wem@@ eine Runde "+taenze[ifoo]+".";
+ out_vic="@@name@@ reisst Dich an sich und tanzt@@adverb@@ eine Runde "
+ +taenze[ifoo]+" mit Dir.";
+ out_oth="@@name@@ schnappt sich @@wen@@ und die beiden tanzen"
+ +"@@adverb@@ eine Runde "+taenze[ifoo]+".";
+ }
+ return FeelIt();
+
+ /**************** Traeumen ***************/
+ case "traeum":
+ HELPCHECK("traeum");
+ if (!str)
+ Return("Traeume wovon oder von wem?\n");
+ ParseRest(str);
+ out_sel="Du traeumst"+(who ? "@@adverb@@ von @@wem@@."
+ : (adverb ? "@@adverb@@." : " "+str+"."));
+ if (who) out_vic="@@gname@@ traeumt@@adverb@@ von Dir.";
+ out_oth="@@gname@@ traeumt"+(who ? "@@adverb@@ von @@wem@@."
+ : (adverb ? "@@adverb@@." : " "+str+"."));
+ return FeelIt();
+
+ /**************** Treten (tritt) ***************/
+ case "tritt":
+ case "tret":
+ HELPCHECK("tritt");
+ if (!str) {
+ GHOSTCHECK("Dein Fuss faehrt durch die beruehmte langvergessene "
+ +"unsichtbare Schildkroete\nhindurch.\n",
+ gname()+" will die beruehmte langvergessene unsichtbare\n"
+ +"Schildkroete treten, aber "+QPP(MALE,WER)
+ +" Fuss faehrt durch sie hindurch.\n", 0);
+ out_sel="Du trittst die beruehmte langvergessene unsichtbare "
+ +"Schildkroete.";
+ out_oth="@@gname@@ tritt die beruehmte langvergessene unsichtbare "
+ +"Schildkroete.";
+ }
+ else {
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du trittst @@alle@@@@adverb@@. Solltest Du nicht langsam "
+ +"an Flucht denken?";
+ out_vic="@@name@@ tritt @@alle@@@@adverb@@. Traut sich ganz "
+ +"schoen was!";
+ return MixedOut(WEN);
+ }
+ if (!who && !adverb)
+ Return("Wenn Du schon was nach tritt tippst, dann sag mir, wen "
+ +"oder wie ich das soll.\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Du schaffst es nicht, Dir selber in den Hintern zu treten.",
+ "Tote Sachen tritt man nicht auch noch!"))
+ return 1;
+ if (who) {
+ out_sel="Du trittst@@ wen@@@@adverb@@.";
+ if (who) out_vic="@@gname@@ tritt Dich@@adverb@@.";
+ out_oth="@@gname@@ tritt@@ wen@@@@adverb@@.";
+ }
+ else {
+ out_sel="Du trittst die beruehmte langvergessene unsichtbare "
+ +"Schildkroete@@adverb@@.";
+ out_oth="@@gname@@ tritt die beruehmte langvergessene unsichtbare "
+ +"Schildkroete\n@@adverb@@.";
+ }
+ }
+ return FeelIt();
+
+ /**************** Troesten ***************/
+ case "troest":
+ HELPCHECK("troest");
+ ParseRest(str);
+ if (!who)
+ Return("Wen willst Du troesten?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Trost musst Du bei jemand anders suchen.",
+ "Das Teil musst Du nicht troesten, das fuehlt nix."))
+ return 1;
+ out_sel="Du troestest@@ wen@@@@adverb@@.";
+ out_vic="@@gname@@ troestet Dich@@adverb@@.";
+ out_oth="@@gname@@ troestet@@ wen@@@@adverb@@.";
+ return FeelIt();
+
+ /**************** Umarmen ***************/
+ case "umarm":
+ HELPCHECK("umarm");
+ ParseRest(str);
+ if (!who)
+ Return("Wen willst Du umarmen?\n");
+ if (who && CheckLife(0,NOT_DEAD,0,"Bitte umarme nur Lebewesen."))
+ return 1;
+ if (ghost() && CheckLife(NOT_SELF,0,
+ "Du kannst Dich als Geist nicht selber waermen."))
+ return 1;
+ str1=who->QueryProp(P_NAME);
+ if(pointerp(str1))str1=(string)str1[0]; // Rumata
+ str2=who->QueryPronoun(WEN);
+ GHOSTCHECK("Du willst "+str1+" umarmen, aber Deine Arme gehen durch "
+ +str2+" durch.\n",
+ gname()+" will "+str1+" umarmen, aber "+QPP(MALE,WER,PLURAL)
+ +" Arme gehen\ndurch "+str2+" hindurch.\n",
+ gname()+" will Dich umarmen, aber "+QPP(MALE,WER,PLURAL)
+ +" Arme gehen durch Dich hindurch.\n");
+ if (for_all) {
+ out_sel="Du umarmst @@alle@@@@adverb@@.";
+ out_vic="@@name@@ umarmt @@alle@@@@adverb@@.";
+ return MixedOut(WEN);
+ }
+ if (who==this_player()) {
+ out_sel="Du legst Deine Arme um Dich und waermst Dich "
+ +"ein bisschen selber.";
+ out_oth="@@name@@ legt "+QPP(MALE,WER,PLURAL)
+ +" Arme um sich und waermt sich ein bisschen selber.";
+ }
+ else {
+ out_sel="Du umarmst@@ wen@@@@adverb@@.";
+ out_vic="@@name@@ umarmt Dich@@adverb@@.";
+ out_oth="@@name@@ umarmt@@ wen@@@@adverb@@.";
+ }
+ return FeelIt();
+
+ /**************** Verfluchen ***************/
+ case "verfluch":
+ HELPCHECK("verfluch");
+ if (!str)
+ Return("Wen oder was willst Du denn verfluchen?\n");
+ ParseRest(str);
+ if (!who) {
+ out_sel="Du verfluchst "+str+".";
+ out_oth="@@gname@@ verflucht "+str+".";
+ }
+ else {
+ if (who==this_player())
+ Return("Sich selber verflucht man besser nicht...\n");
+ if (!adverb) {
+ flag=sscanf(str, "%s %s", str1,str2);
+ out_sel="Du verfluchst@@ wen@@"+(flag==2 ? " "+str2 : "")+".";
+ out_vic="@@gname@@ verflucht Dich"+(flag==2?" "+str2 : "")+".";
+ out_oth="@@gname@@ verflucht@@ wen@@"+(flag==2 ? " "+str2 : "")+".";
+ }
+ else {
+ out_sel="Du verfluchst@@ wen@@@@adverb@@.";
+ out_vic="@@gname@@ verflucht Dich@@adverb@@.";
+ out_oth="@@gname@@ verflucht@@ wen@@@@adverb@@.";
+ }
+ }
+ return FeelIt();
+
+ /**************** Verneigen / Verbeugen ***************/
+ case "verneig":
+ case "verbeug":
+ HELPCHECK("verneig");
+ GHOSTCHECK("Du verneigst Dich ein bisschen heftig - Dein Kopf taucht "
+ +"kurz in den Boden.\n",
+ gname()+" verneigt sich. Ein bisschen heftig - "+QPP(MALE,WER)
+ +" Kopf\ntaucht kurz in den Boden ein.\n", 0);
+ if ((!str) || (str == "dich")) {
+ out_sel="Du verneigst Dich vor den Anwesenden.";
+ out_oth="@@name@@ verneigt sich anmutig.";
+ }
+ else {
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du verneigst Dich@@adverb@@ vor @@alle@@.";
+ out_vic="@@name@@ verneigt sich@@adverb@@ vor @@alle@@.";
+ return MixedOut(WEM);
+ }
+ if (!who && !adverb)
+ Return("Verneige dich irgendwie oder vor jemandem.\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Wie willst Du das denn schaffen?",
+ "Vor Sachen wird hier nicht verneigt, klar?\n"))
+ return 1;
+ out_sel="Du verneigst Dich@@adverb@@"+(who ? " vor" : "")+"@@ wem@@.";
+ if (who ) out_vic="@@name@@ verneigt sich@@adverb@@ vor Dir.";
+ out_oth="@@name@@ verneigt sich@@adverb@@"+(who ? " vor" : "")
+ +"@@ wem@@.";
+ }
+ return FeelIt();
+
+ /**************** Verneinen ***************/
+ case "nein":
+ case "noe":
+ HELPCHECK("nein");
+ GHOSTCHECK("Du schuettelst Deinen Kopf so heftig, dass er kurz "
+ +"davonschwebt.\n",
+ gname()+" schuettelt heftig den Kopf.\nSo heftig, dass dieser "
+ +"kurz davonschwebt und wieder eingefangen werden muss.\n", 0);
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Schuettle wie den Kopf?\n");
+ out_sel="Du schuettelst@@adverb@@ den Kopf.";
+ out_oth="@@name@@ schuettelt@@adverb@@ den Kopf.";
+ return FeelIt();
+
+ /**************** Wackeln ***************/
+ case "wackel":
+ case "wackl":
+ HELPCHECK("wackel");
+ if (ghost())
+ Return("Da gibt es nichts mehr, womit Du wackeln kannst.\n");
+ if (str)
+ if (strstr(str, "mit ")==0)
+ sscanf(str, "mit %s", sfoo);
+ else if (strstr(str,"mit ")>0) {
+ sscanf(str, "%s mit %s", sfoo, sfoo);
+ flag=1;
+ }
+ if (sfoo=="") sfoo=0;
+ ParseRest(str, (sfoo ? (flag ? " mit " : "mit ")+sfoo : 0));
+ if (str && !adverb && !sfoo)
+ Return("Wackle wie oder womit?\n");
+ out_sel="Du wackelst@@adverb@@ mit "+(sfoo ? sfoo+"." : "dem Hintern.");
+ out_oth="@@name@@ wackelt@@adverb@@ mit "
+ +(sfoo ? sfoo+"." : QPP(MALE,WEM)+" Hintern.");
+ return FeelIt();
+
+ /**************** Waelzen ***************/
+ case "waelz":
+ HELPCHECK("waelz");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Waelze Dich wie auf dem Boden?\n");
+ out_sel="Du waelzt Dich"+(adverb ? "@@adverb@@" : " vor Lachen")
+ +" auf dem Boden.";
+ out_oth="@@gname@@ waelzt sich"+(adverb ? "@@adverb@@" : " vor Lachen")
+ +(ghost() ? " im" : " auf dem")+" Boden.";
+ return FeelIt();
+
+ /**************** Warten ***************/
+ case "wart":
+ HELPCHECK("wart");
+ ParseRest(str);
+ if (!str) {
+ out_sel="Du tippst mit dem Fuss auf den Boden.";
+ out_oth="@@gname@@ tippt mit dem Fuss auf den Boden.";
+ } else if (!who && adverb) {
+ out_sel="Du wartest@@adverb@@.";
+ out_oth="@@gname@@ wartet@@adverb@@.";
+ } else {
+ out_sel="Du wartest@@adverb@@ auf "+(who ? "@@wen@@." : str+".");
+ if (who) out_vic="@@gname@@ wartet@@adverb@@ auf Dich.";
+ out_oth="@@gname@@ wartet@@adverb@@ auf "+(who ? "@@wen@@." : str+".");
+ }
+ return FeelIt();
+
+#ifdef WECKE
+ /**************** Wecken ***************/
+ case "weck":
+ HELPCHECK("weck");
+ if (ParseRemote(str))
+ return 1;
+// ParseRest(str);
+ if (!who)
+ Return("Wen willst Du wecken?\n");
+ if (sscanf(str, "%s %s", sfoo, sfoo)==2)
+ flag=1;
+ out_sel="Dein Wecker klingelt bei @@wem@@@@adverb@@"
+ +(adverb ? "." : (flag ? ": "+sfoo : "."));
+ out_vic=" "+name(WESSEN)+" Wecker klingelt bei Dir@@adverb@@"
+ +(adverb ? "." : (flag ? ": "+sfoo : "."));
+ out_oth="@@gname@@ wirft "+QPP(MALE, WEN)
+ +" Wecker@@adverb@@ nach @@wem@@.";
+ if (!who->QueryProp(P_VISUALBELL))
+ out_vic[0]=7; // chr(7)==BEL
+ else out_vic=out_vic[1..];
+ return FeelIt();
+#endif
+
+ /**************** Weinen ***************/
+ case "wein":
+ HELPCHECK("wein");
+ GHOSTCHECK("Es reicht leider nur fuer ein paar winzige Nebelwoelkchen, "
+ +"nicht fuer Traenen.\n",
+ gname()+" verzieht das Gesicht und ein paar winzige Nebel-\n"
+ +"woelkchen entfernen sich von seinen \"Augen\".\n", 0);
+ if (!str) {
+ out_sel="Waaaaah! Du weinst bitterlich.";
+ out_oth="@@name@@ bricht in Traenen aus und weint bitterlich.";
+ }
+ else {
+ ParseRest(str);
+ if (!who && !adverb)
+ Return("Weine Dich irgendwie bei irgendwem aus, aber nicht so.\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Bei sich selber kann man sich so schlecht ausweinen.",
+ "Bei wem willst Du Dich ausweinen???"))
+ return 1;
+ if (who) {
+ out_sel="Du weinst Dich@@adverb@@ bei@@ wem@@ aus.";
+ out_vic="@@name@@ weint sich@@adverb@@ bei Dir aus.";
+ out_oth="@@name@@ weint sich@@adverb@@ bei@@ wem@@ aus.";
+ }
+ else {
+ out_sel="Du brichst in Traenen aus und weinst@@adverb@@.";
+ out_oth="@@name@@ bricht in Traenen aus und weint@@adverb@@.";
+ }
+ }
+ return FeelIt();
+
+ /**************** Winken ***************/
+ case "wink":
+ HELPCHECK("wink");
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du winkst @@alle@@@@adverb@@ zu.";
+ out_vic="@@name@@ winkt @@alle@@@@adverb@@ zu.";
+ return MixedOut(WEM);
+ }
+ if (!who && !adverb && str)
+ Return("Vielleicht solltest Du auch sagen, wem oder wie Du "
+ +"(zu)winken willst.\n");
+ if (who && CheckLife(NOT_SELF, NOT_DEAD,
+ "Wink Dir nicht selber zu.",
+ "Du musst schon einem Lebewesen zuwinken."))
+ return 1;
+ out_sel="Du winkst@@ wem@@@@adverb@@"+(who ? " zu" : "")+".";
+ if (who) out_vic="@@gname@@ winkt Dir@@adverb@@ zu.";
+ out_oth="@@gname@@ winkt@@ wem@@@@adverb@@"+(who ? " zu" : "")+".";
+ return FeelIt();
+
+ /**************** Wuergen ***************/
+ case "wuerg":
+ HELPCHECK("wuerg");
+ ParseRest(str);
+ if (str && !who && !adverb)
+ Return("Wuerge wen oder wie?\n");
+ if (!who) {
+ out_sel="Du faengst@@adverb@@ an zu wuergen.";
+ out_oth="@@gname@@ faengt@@adverb@@ an zu wuergen.";
+ } else if (CheckLife(NOT_SELF, NOT_DEAD,
+ "Du wuergst ein bischen an Dir rum. Dir wird schnell langweilig.",
+ "Wuerg lieber ein Lebewesen.")) {
+ return 1;
+ } else {
+ out_sel="Du springst @@wen@@ an und faengst an, "+who->QueryPronoun(WEN)
+ +"@@adverb@@ zu wuergen.";
+ out_vic="@@gname@@ springt Dich auf einmal an und wuergt Dich@@adverb@@.";
+ out_oth="@@gname@@ springt auf einmal @@wen@@ an und wuergt "
+ +who->QueryPronoun(WEN)+"@@adverb@@.";
+ }
+ return FeelIt();
+
+ /**************** Wundern ***************/
+ case "wunder":
+ HELPCHECK("wunder");
+ ParseRest(str);
+ if (str && !who && !adverb)
+ Return("Wie oder ueber wen willst Du Dich wundern?\n");
+ out_sel="Du wunderst Dich@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+ if (who) out_vic="@@gname@@ wundert sich@@adverb@@ ueber Dich.";
+ out_oth="@@gname@@ wundert sich@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+ return FeelIt();
+
+ /**************** Wuscheln ***************/
+ case "wuschel":
+ case "wuschl":
+ HELPCHECK("wuschel");
+ ParseRest(str);
+ if (!who)
+ Return("Wen willst Du denn wuscheln?\n");
+ if (CheckLife(0,NOT_DEAD,
+ "", "Hmm, sehr tot. Ne, lieber nicht."))
+ return 1;
+ if (who->QueryProp(P_FROG)) {
+ write("Du verwuschelst... aeh... hm. Ein Frosch hat wohl nix "
+ +"zum Wuscheln.\n");
+ return 1;
+ };
+ GHOSTCHECK("Du willst "+who->name(WEN)+" wuscheln - aber Deine "
+ +"Haende gehen\nglatt durch den Kopf durch.\n",
+ gname()+" will "+who->name(WEN)+" den Kopf wuscheln, aber "
+ +"die Geister-\nhaende gehen glatt durch den Kopf durch.\n",
+ gname()+" will Dich wuscheln, aber diese Geisterhaende "
+ +"gehen\nglatt durch Deinen Kopf durch - Du hast ein seltsames "
+ +"Gefuehl dabei.\n");
+ if (member(({"highlander","boing","mieze","freund"}), who->query_real_name())>-1)
+ switch (who->query_real_name()) {
+ case "highlander": str1="Federn"; break;
+ case "freund" :
+ case "mieze" :
+ case "boing" : str1="Fell"; break;
+ }
+ else if (who->is_class_member(({CL_DRAGON, CL_FISH, CL_REPTILE})))
+ str1="Schuppen";
+ else if (who->is_class_member(({CL_BIRD, "elster","greif"})))
+ str1="Federn";
+ else if (who->is_class_member(({CL_MAMMAL_LAND,CL_FELINE,"tiger",
+ "steinbeisser","knuddeleisbaer"})))
+ str1="Fell";
+ else str1="Haare";
+ out_sel="Du verwuschelst@@adverb@@ @@wessen@@ "+str1+".";
+ out_vic="@@name@@ verwuschelt@@adverb@@ Dein"
+ +(str1=="Fell" ? " " : "e ")+str1+".";
+ out_oth="@@name@@ verwuschelt@@adverb@@ @@wessen@@ "+str1+".";
+ return FeelIt();
+
+ /**************** Zitieren ***************/
+ case "zitier":
+ HELPCHECK("zitier");
+ ParseRest(str);
+ if (!str)
+ Return("Zitiere was oder wen womit?\n");
+ sfoo=implode(explode(str, " ")[1..], " ");
+ if (sfoo=="") sfoo=0;
+ if (who) {
+ out_sel="Du zitierst @@wen@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+ out_vic="@@gname@@ zitiert Dich"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+ out_oth="@@gname@@ zitiert @@wen@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+ }
+ else {
+ sfoo=explode(str, "/")[0];
+ out_sel="Du zitierst@@adverb@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+ out_oth="@@gname@@ zitiert@@adverb@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+ }
+ return FeelIt();
+
+ /**************** Zittern ***************/
+ case "zitter":
+ HELPCHECK("zitter");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Zittere wie?\n");
+ out_sel="Du zitterst"+(adverb ? "@@adverb@@." : " vor Angst.");
+ out_oth="@@gname@@ zittert"+(adverb ? "@@adverb@@." : " vor Angst.");
+ return FeelIt();
+
+ /**************** Schulterzucken ***************/
+ case "zuck" :
+ HELPCHECK("zuck");
+ if (str)
+ if (sscanf(str,"%s mit den schultern",sfoo))
+ str=sfoo;
+ else if (sscanf(str,"%s den schultern",sfoo))
+ str=sfoo;
+ else
+ if (sscanf(str,"%s schultern",sfoo))
+ str=sfoo;
+ if (str=="") str=0;
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Zucke wie mit den Schultern?\n");
+ out_sel="Du zuckst@@adverb@@ mit den Schultern.";
+ out_oth="@@gname@@ zuckt"+(adverb ? "@@adverb@@" : " ratlos")
+ +" mit den Schultern.";
+ return FeelIt();
+
+ /**************** Zwinkern ***************/
+ case "zwinker":
+ HELPCHECK("zwinker");
+ if (ghost())
+ Return("Vergiss es - das ist als Geist viel zu unauffaellig, als dass\n"
+ +"es andere Leute sehen wuerden.\n");
+ ParseRest(str);
+ if (str && !who && !adverb)
+ Return("Zwinkere wie? Zwinkere wem zu?\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Du kannst Dir nicht selber zuzwinkern.",
+ "Wieso reagiert das Ding da nicht auf Dein Zwinkern? Ist es etwa tot?"))
+ return 1;
+ out_sel="Du zwinkerst@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+ if (who) out_vic="@@name@@ zwinkert Dir@@adverb@@ zu.";
+ out_oth="@@name@@ zwinkert@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+ return FeelIt();
+
+ /**************** Zunge rausstrecken ***************/
+ case "streck":
+ HELPCHECK("streck");
+ GHOSTCHECK("Sorry, Du hast keine Zunge zum Rausstrecken.\n","",0);
+ if (!str)
+ Return("Strecke was wie wem wo wann wieso?\n");
+ str=lower_case(str);
+ if (sscanf(str, "%s zunge raus", str1)!=1 &&
+ sscanf(str, "%s die zunge raus", str1)!=1)
+ Return("Strecke was wie wem wo wann wieso?\n");
+ ParseRest(str1);
+ if (for_all) {
+ out_sel="Du streckst @@alle@@@@adverb@@ die Zunge raus.";
+ out_vic="@@name@@ streckt @@alle@@@@adverb@@ die Zunge raus.";
+ return MixedOut(WEM);
+ }
+ out_sel="Du streckst@@ wem@@@@adverb@@ die Zunge raus.";
+ if (who) out_vic="@@name@@ streckt Dir@@adverb@@ die Zunge raus.";
+ out_oth="@@name@@ streckt@@ wem@@@@adverb@@ die Zunge raus.";
+ return FeelIt();
+
+ // Spezialsachen - Geisterverben und Magierverben
+
+ /**************** Rasseln ***************/
+ case "rassel":
+ case "rassl":
+ if (!ghost())
+ Return("Das ist nicht Dein Fachgebiet - Du bist doch kein Geist!\n");
+ HELPCHECK("rassel");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Rassel wie?\n");
+ out_sel="Du rasselst"+(adverb ? "@@adverb@@" : " fuerchterlich")
+ +" mit einer rostigen Rasselkette,\n"
+ +"die Du auf einmal fuer einen Moment in der Hand haeltst.";
+ out_oth="@@gname@@ holt auf einmal eine rostige Rasselkette aus\n"
+ +"dem Nichts und faengt an,"+(adverb ? "@@adverb@@" : " fuerchterlich")
+ +" damit zu rasseln.\n"
+ +"Danach ist die Kette auf einmal wieder verschwunden.";
+ return FeelIt();
+
+ /**************** Heulen ***************/
+ case "heul":
+ if (!ghost())
+ Return("Lass das mal den Fachleuten (also den Geistern).\n");
+ HELPCHECK("heul");
+ ParseRest(str);
+ if (str && !adverb)
+ Return("Heule wie?\n");
+ out_sel="Du heulst"+(adverb ? "@@adverb@@." : " schauerlich.");
+ out_oth="@@gname@@ heult"+(adverb ? "@@adverb@@." : " schauerlich.");
+ return FeelIt();
+
+ /**************** Treten (tretet) ***************/
+ case "kick":
+ if (!IS_WIZARD(this_player()))
+ return 0;
+ HELPCHECK("kick");
+ if (!str) {
+ GHOSTCHECK("Dein Fuss faehrt durch die beruehmte langvergessene "
+ +"unsichtbare Schildkroete\nhindurch.\n",
+ gname()+" will die beruehmte langvergessene unsichtbare\n"
+ +"Schildkroete treten, aber "+QPP(MALE,WER)
+ +" Fuss faehrt durch sie hindurch.\n", 0);
+ out_sel="Du tretest die beruehmte langvergessene unsichtbare "
+ +"Schildkroete.";
+ out_oth="@@name@@ tretet die beruehmte langvergessene unsichtbare "
+ +"Schildkroete.";
+ }
+ else {
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du tretest @@alle@@@@adverb@@.";
+ out_vic="@@name@@ tretet @@alle@@@@adverb@@.";
+ return MixedOut(WEN);
+ }
+ if (!who && !adverb)
+ Return("Wenn Du schon was nach kick tippst, dann sag mir wen "
+ +"oder wie ichdas soll.\n");
+ if (who && CheckLife(NOT_SELF,NOT_DEAD,
+ "Du schaffst es nicht, Dir selber in den Hintern zu treten.",
+ "Tote Sachen tritt man nicht auch noch!"))
+ return 1;
+ if (who) {
+ out_sel="Du tretest@@ wen@@@@adverb@@.";
+ if (who) out_vic="@@gname@@ tretet Dich@@adverb@@.";
+ out_oth="@@gname@@ tretet@@ wen@@@@adverb@@.";
+ }
+ else {
+ out_sel="Du tretest die beruehmte langvergessene unsichtbare "
+ +"Schildkroete@@adverb@@.";
+ out_oth="@@gname@@ tretet die beruehmte langvergessene unsichtbare "
+ +"Schildkroete\n@@adverb@@.";
+ }
+ }
+ return FeelIt();
+
+ /************* Nassspritzen ***************/
+ case "splash":
+ if (!IS_WIZARD(this_player()) &&
+ !(IS_SEER(this_player()) && present("SEHER\nspritzpistole",this_player())))
+ return 0;
+ HELPCHECK("splash");
+ ParseRest(str);
+ if (for_all) {
+ out_sel="Du ziehst Deine Wasserpistole und spritzt @@alle@@@@adverb@@ "
+ +"patschnass.";
+ out_vic="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt\n"
+ +"@@alle@@@@adverb@@ patschnass.";
+ return MixedOut(WEN);
+ }
+ if (!who)
+ Return("Wen willst Du denn nassmachen?\n");
+ if (who == this_player()) {
+ out_sel="Sag mal, kommst Du Dir nicht ein bisschen doof vor?\n"
+ +"Du ziehst Deine Wasserpistole und spritzt Dich@@adverb@@ selber patschnass.";
+ out_oth="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt "
+ +"sich@@adverb@@ aus unerfindlichen Gruenden selbst patschnass.";
+ }
+ else {
+ out_sel="Du ziehst Deine Wasserpistole und spritzt @@wen@@@@adverb@@ "
+ +"patschnass.";
+ out_vic="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt "
+ +"Dich@@adverb@@ patschnass.";
+ out_oth="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt "
+ +"@@wen@@@@adverb@@ patschnass.";
+ }
+ return FeelIt();
+
+ /**************** Anflammen ***************/
+ case "flam":
+ if (!IS_WIZARD(this_player()) &&
+ !(IS_SEER(this_player()) && present("SEHER\nflammenwerfer",this_player())))
+ return 0;
+ HELPCHECK("flame");
+ if (ghost())
+ Return("Du hast leider grade Deinen Flammenwerfer nicht dabei.\n");
+ ParseRest(str);
+ ifoo=!random(7);
+ if (for_all) {
+ out_sel="Du holst aus Deinen tiefsten Taschen (oder was weiss denn "
+ +"ich woher) Deinen\nMorgenGrauen handgearbeiteten Mini-Flammenwerfer "
+ +"(tm), richtest ihn aus und...\n"
+ +(ifoo ? "schaust leicht frustriert auf das Streichholz, in das "
+ +"er sich verwandelt hat."
+ : "feuerst@@adverb@@ einen riesigen Feuerball auf @@alle@@ ab.\n"
+ +"Es riecht auf einmal so verbrannt hier...");
+ out_vic="@@name@@ holt auf einmal irgendwoher einen MorgenGrauen "
+ +"handgearbeiteten\nMini-Flammenwerfer (tm), richtet ihn aus und...\n"
+ +(ifoo ? "schaut ziemlich frustriert auf das Streichholz, in das "
+ +"sich das Ding verwandelt hat."
+ : "feuert@@adverb@@ einen riesigen Feuerball auf @@alle@@ ab.\n"
+ +"Dir wird so warm um's Herz...");
+ return MixedOut(WEN);
+ }
+ if (!who)
+ Return("Wen willst Du denn ankokeln?\n");
+ out_sel="Du holst aus Deinen tiefsten Taschen (oder was weiss denn "
+ +"ich woher) Deinen\nMorgenGrauen handgearbeiteten Mini-Flammenwerfer "
+ +"(tm), richtest ihn aus und...\n"
+ +(ifoo ? "schaust leicht frustriert auf das Streichholz, in das er "
+ +"sich verwandelt hat."
+ : "feuerst@@adverb@@ einen riesigen Feuerball auf @@wen@@ ab.\n"
+ +"Es riecht auf einmal so verbrannt hier...");
+ out_vic="@@name@@ holt auf einmal irgendwoher einen MorgenGrauen "
+ +"handgearbeiteten\nMini-Flammenwerfer (tm), richtet ihn auf Dich "
+ +"aus und...\n"
+ +(ifoo ? "schaut ziemlich frustriert auf das Streichholz, in das "
+ +"sich das Ding\nverwandelt hat."
+ : "feuert@@adverb@@ einen riesigen Feuerball auf Dich ab.\n"
+ +"Dir wird so warm ums Herz...");
+ out_oth="@@name@@ holt auf einmal irgendwoher einen MorgenGrauen "
+ +"handgearbeiteten\nMini-Flammenwerfer (tm), richtet ihn "
+ +"auf@@ wen@@ aus und...\n"
+ +(ifoo ? "schaut ziemlich frustriert auf das Streichholz, in das "
+ +"sich das Ding\nverwandelt hat."
+ : "feuert@@adverb@@ einen riesigen Feuerball auf@@ wen@@ ab.\nEs "
+ +"riecht auf einmal irgendwie verbrannt hier ...");
+ return FeelIt();
+
+ // Special 2: remote verbs
+
+ /**************** Remote knuddeln ***************/
+ case "rknuddel":
+ case "rknuddl":
+ HELPCHECK("rknuddel");
+ if (ParseRemote(str))
+ return 1;
+ if (!who)
+ Return("Knuddle wen?\n");
+ if (CheckLife(NOT_SELF,0,
+ "Das bringt doch nix, lass es halt.",
+ 0))
+ return 1;
+ if (present(who, environment()))
+ Return("Wenn jemand neben Dir steht, nimm knuddel.\n");
+ out_sel="Du knuddelst @@wen@@@@adverb@@ aus der Ferne.";
+ out_vic="@@gname@@ knuddelt Dich@@adverb@@ aus der Ferne.";
+ return FeelIt();
+
+ /**************** Remote winken ***************/
+ case "rwink":
+ HELPCHECK("rwink");
+ if (ParseRemote(str))
+ return 1;
+ if (!who)
+ Return("Winke wem zu?\n");
+ if (CheckLife(NOT_SELF,0,
+ "Sehr witzig. Pah.", 0))
+ return 1;
+ if (present(who, environment()))
+ Return("Wenn jemand neben Dir steht, nimm wink.\n");
+ out_sel="Du winkst @@wem@@@@adverb@@ aus der Ferne zu.";
+ out_vic="@@gname@@ winkt Dir@@adverb@@ aus der Ferne zu.";
+ return FeelIt();
+
+ /**************** Verbenliste ***************/
+ case "verb":
+ case "verben":
+ HELPCHECK("verb");
+ More(SOULHELP->Help());
+ return 1;
+
+ /**************** Adverbienverwaltung ***************/
+ case "adverb":
+ case "adverben":
+ case "adverbien": { /* Das ist die richtige Form, aber wer weiss das? */
+ string f1,f2;
+ HELPCHECK("adverb");
+ if (!str || str=="#" || str=="$")
+ return zeige_adverbs((str=="#" ? 1 : (str=="$" ? 2 : 0)));
+ if (sscanf(str, "%s %s", f1,f2)==2) {
+ f1 = lower_case(f1); // kleingeschrieben speichern, spart Umwandlung
+ if (f1=="")
+ Return("Hm, da muss wohl ein Leerzeichen zu viel gewesen sein. Bitte "
+ +"nochmal,\naber ohne zuviele Leerzeichen.\n");
+ if (f1=="?") {
+ f2 = lower_case(f2);
+ string match;
+ if ((match=QueryStdAdverbs()[f2] || plr_adverbs[f2]))
+ write("Die Abkuerzung "+f2+" gehoert zu dem Adverb:\n"+match+LF);
+ else
+ write("Diese Abkuerzung ist bisher nicht definiert.\n");
+ return 1;
+ }
+ if (QueryStdAdverbs()[f1])
+ Return("Die Standardabkuerzungen koennen nicht neu definiert "
+ +"werden.\n");
+ if (sizeof(plr_adverbs)>=100)
+ {
+ write("Mehr als 100 eigene Adverbien kannst Du nicht definieren.\n");
+ return 1;
+ }
+ if (plr_adverbs[f1]) {
+ plr_adverbs[f1]=f2;
+ write("OK, Adverb mit der Abkuerzung \""+f1+"\" auf \""+f2
+ +"\" gesetzt.\n");
+ }
+ else {
+ if (sizeof(f1) > 6)
+ Return("Die Abkuerzung ist zu lang, bitte nicht mehr als "
+ +"6 Zeichen.\n");
+ plr_adverbs[f1]=f2;
+ write("OK, neues Adverb \""+f2+"\" mit der Abkuerzung \""+f1+"\".\n");
+ }
+ }
+ else {
+ str = lower_case(str);
+ if (QueryStdAdverbs()[str])
+ Return("Die Standardadverben koennen nicht geloescht werden.\n");
+ else if (!plr_adverbs[str])
+ Return("Du hast kein Adverb mit dieser Abkuerzung.\n"
+ +"Syntax: adverb, um die Adverbien anzuzeigen,\n"
+ +" adverb #, um nur Deine Adverbien anzuzeigen,\n"
+ +" adverb $, um nur die Standardadverbien anzuzeigen,\n"
+ +" adverb ? <Abkuerzung>, um nachzusehen, ob <Abkuerzung> "
+ +"definiert ist,\n"
+ +" adverb <Abkuerzung> <Adverb>, um der <Abkuerzung> das "
+ +"<Adverb>\n"
+ +" zuzuordnen,\n"
+ +" adverb <Abkuerzung>, um das Adverb mit der <Abkuerzung> "
+ +"zu loeschen,\n");
+ else {
+ write("OK, Adverb \""+plr_adverbs[str]+"\" geloescht.\n");
+ plr_adverbs=m_copy_delete(plr_adverbs, str);
+ }
+ }
+ return 1;
+ }
+ }
+ return(0); //fallthrough
+}
+
diff --git a/std/player/soulhelp.c b/std/player/soulhelp.c
new file mode 100644
index 0000000..27a48f9
--- /dev/null
+++ b/std/player/soulhelp.c
@@ -0,0 +1,317 @@
+// MorgenGrauen MUDlib
+//
+// player/soulhelp.c -- Hilfe zu den Soulkommandos
+//
+// $Id: soulhelp.c 7423 2010-02-07 22:56:38Z Zesstra $
+
+#pragma strong_types,save_types
+
+#include <wizlevels.h>
+#ifdef WECKE
+#undef WECKE
+#endif
+#define WECKE
+
+static string *wizcmds, *plrcmds, *ghostcmds;
+static mapping help;
+
+private string HelpVerb(string v);
+private string* SortIt(string *arr);
+
+
+/**
+ Initialisierung
+*/
+void create() {
+ plrcmds=({
+ "zuck", "schmieg", "antworte", "applaudiere",
+ "betaste", "cls", "drehe (daeumchen)", "danke",
+ "druecke", "erroete", "flippe", "frage",
+ "furze", "gaehne", "gluckse", "grinse",
+ "guck", "haetschel", "hickse", "huepfe",
+ "huste", "keuche", "kichere", "klatsche",
+ "knabbere", "knickse", "knirsche", "knurre",
+ "knutsche", "kotze", "kuesse", "lache",
+ "laechle", "liebe", "nicke", "niese",
+ "gib", "pfeife", "ruelpse", "runzle",
+ "schmolle", "schmunzle", "schnippe", "schnarche",
+ "schnurre", "schreie", "schuettle", "seufze",
+ "singe", "sniefe/schniefe", "spucke", "starre",
+ "staune", "stiere", "stoehne", "stosse",
+ "streichle", "tanze", "tritt", "troeste",
+ "umarme", "verneige", "wackle", "waelze",
+ "weine", "winke", "zwinkere", "verben",
+ "aechze", "erbleiche", "fluche", "verfluche",
+ "kitzle", "nein", "deute", // "zeige",
+ "denke [text]", "knuddle", "taetschel", "wuschel",
+ "strecke ... [die] zunge raus", "kratz",
+ "grummel", "jubel / juble ... [zu]", "wuerg",
+ "gratuliere / beglueckwuensche", "raeusper",
+ "argl", "rotfl", "grunz", "kuschel", "atme ... auf",
+ "freue", "sniff", "grueble", "bohre ... [in der nase]",
+ "putze [nase]", "bibbere", "quietsche/quieke", "schluchze",
+ "schnaufe", "schnaube", "philosophiere", "sabbere",
+ "stimme [...] zu", "krieche", "mustere", "schaeme",
+ "schnalze ... [zunge]", "stampfe ... [auf]", "zitiere", "lobe",
+ "quake", "reibe ... [die] Augen|Haende", "stutze", "schweige",
+ "klopfe", "wundere", "stupse", "brummel", "entschuldige",
+ "mopper", "zeige", "traeume", "begruesse","jammer",
+ });
+#ifdef WECKE
+ plrcmds+=({"wecke"});
+#endif
+ wizcmds=({
+ "kick", "splash", "flame",
+ });
+ ghostcmds=({
+ "rassel/rassle", "heule", "erschrecke",
+ });
+ // Aufbau des help-mappings:
+ // key : Verb, wie es in soul.c in CHECK_HELP geschrieben ist
+ // entry 1: &n = [<Name>] &a = [<Adverb>] &t=[<Text>}
+ // ! danach heisst noetige Angabe, also ohne []
+ // entry 2: &g = Verhaelt sich bei Geistern anders
+ // &a = Man kann "alle" als Ziel angeben
+ // &d = Defaultadverb; muss am Ende angegeben werden
+ help=([
+ "zuck" : "&a [[mit [den]] schultern]"; "Gibt man nicht mindestens "
+ +"\"schultern\" an, so zuckt man zusammen. Das Defaultadverb gilt "
+ +"nur fuer das Schulterzucken.&dratlos",
+ "schmieg" : "&n &a"; "",
+ "antwort" : "&n &t!"; "",
+ "applaudier" : "&n &a"; "&g&a",
+ "begruess" : "&n! &a"; "",
+ "betast" : "&n! &a"; "",
+ "bibber" : "&a"; "",
+ "bohr" : "&a [[in [der]] nase]"; "",
+ "brummel" : "&a | &t"; "",
+ "cls" : ""; "Loescht den Bildschirm auf vt100-kompatiblen Terminals.",
+ "dreh" : "&a [daeumchen | daumen]"; "",
+ "dank" : "&n! &a"; "",
+ "drueck" : "&n! &a"; "&g&a&dzaertlich",
+ "entschuldig" : "&n &a"; "",
+ "erroet" : "&a"; "&g",
+ "erschreck" : "&n! &a"; "&g&dfuerchterlich",
+ "flipp" : "&a"; "&dtotal",
+ "frag" : "&n![|]&t!"; "",
+ "freu" : "&n &a"; "",
+ "furz" : "&a"; "&gHinterlaesst eine duftige Erinnerung im Raum.",
+ "gaehn" : "&a"; "&g",
+ "glucks" : "&a"; "&dwie ein Huhn",
+ "gratulier" : "&n! &a"; "",
+ "grins" : "&n &a"; "&g&a",
+ "gruebel" : "&a"; "",
+ "grummel" : "&a"; "",
+ "guck" : "&a"; "",
+ "haetschel" : "&n! &a"; "&g&a",
+ "hicks" : "&a"; "&g",
+ "huepf" : "&n &a"; "&g",
+ "hust" : "&n &a"; "&g",
+ "jubel" : "&n &a"; "",
+ "jammer" : "&a"; "",
+ "keuch" : "&a"; "&g&dvor Anstrengung",
+ "kicher" : "&n &a"; "",
+ "klatsch" : "&a"; "&g",
+ "klopf" : "&n! &a"; "",
+ "knabber" : "&n! &a"; "&g",
+ "knicks" : "&n &a"; "&g&a",
+ "knirsch" : "&a"; "&g",
+ "knurr" : "&n &a"; "&a",
+ "knutsch" : "&n! &a"; "&g",
+ "kotz" : "&n &a"; "&g",
+ "kriech" : "&n &a"; "",
+ "kuess" : "&n! &a"; "&gKann ausserdem bei bestimmten Gelegenheiten "
+ +"gewisse Nebeneffekte haben.",
+ "lach" : "&n &a"; "&g&a",
+ "laechel" : "&n &a"; "&g&a",
+ "lieb" : "&n! &a"; "",
+ "lob" : "{&n! &a} | &t!"; "Kann einen Nebeneffekt haben.",
+ "mopper" : "&a"; "",
+ "muster" : "&n! &a"; "",
+ "nick" : "&n &a"; "&a&dzustimmend",
+ "nies" : "&a"; "&g",
+ "gib" : "&n! &a [[eine] ohrfeige]"; "&g&a",
+ "pfeif" : "&n &a [nach]"; "&g&danerkennend",
+ "philosophier" : "&a | &t"; "",
+ "putz" : "&a [[die] nase]"; "",
+ "quak" : "&a"; "Geht als Frosch wesentlich besser.",
+ "quiek" : "&a"; "&dvergnuegt",
+ "raeusper" : "&a"; "",
+ "reib" : "&a [[die] Augen] | [[die] Haende]"; "Ohne weitere Angabe reibt "
+ +"man sich die Augen;&dmuede bzw. vergnuegt",
+ "ruelps" : "&a"; "&g",
+ "runzel" : "&a"; "&g",
+ "sabber" : "&n &a"; "",
+ "schaem" : "&a"; "",
+ "schluchz" : "&a"; "&dherzzerreissend",
+ "schmoll" : "&a"; "&g",
+ "schmunzel" : "&a"; "",
+ "schnalz" : "&a [[mit [der]] zunge]"; "",
+ "schnaub" : "&a"; "&dentruestet",
+ "schnauf" : "&a"; "&dvor Anstrengung",
+ "schnipp" : "&a"; "&g",
+ "schnarch" : "&a"; "&g&dlaut",
+ "schnief" : "&a"; "&g",
+ "schnurr" : "&a"; "&dzufrieden",
+ "schrei" : "&n &a"; "&g",
+ "schuettel" : "&n &a"; "&g&aHat einen Unterschied, je nachdem, ob ein Name"
+ +" angegeben wurde oder nicht.",
+ "schweig" : "&a"; "",
+ "seufz" : "&a"; "&g",
+ "sing" : "&a | &t"; "",
+ "sniff" : "&n &a"; "&dtraurig",
+ "spuck" : "&n &a"; "&g",
+ "stampf" : "&a"; "",
+ "starr" : "&n &a"; "Standardadverb gilt hier nur bei Angabe eines Namens."
+ +"&dvertraeumt",
+ "staun" : "&n &a"; "",
+ "stier" : "&n &a"; "&g",
+ "stimm" : "&n &a"; "",
+ "stoehn" : "&a"; "&g",
+ "stoss" : "&n! &a"; "&g",
+ "streichel" : "&n! &a"; "&g&a",
+ "stups" : "&n! &a"; "",
+ "stutz" : "&a"; "",
+ "tanz" : "&n &a"; "&g",
+ "traeum" : "{&n &a} | &t"; "Es koennen Probleme auftreten, wenn ein Adverb"
+ +" in freiem Text erkannt wird. Text wird genau so an \"traeum(s)t\" "
+ +"angehaengt, wie er angegeben wurde.",
+ "tritt" : "&n &a"; "&g&a",
+ "troest" : "&n! &a"; "",
+ "umarm" : "&n! &a"; "&g&a",
+ "verneig" : "&n &a"; "&g&a",
+ "wackel" : "&a [mit &t!]"; "&gMan kann mit allem moeglichem wackeln.",
+ "waelz" : "&a"; "&dvor Lachen",
+ "wart" : "&n &a"; "Man kann auf alles moegliche warten (warte alles "
+ +"moegliche)",
+ "weck" : "&n {&a | &t}"; "Piepst <Name> an und sendet ihm ggf. den Text.",
+ "wein" : "&n &a"; "&g",
+ "wink" : "&n &a"; "&a",
+ "wuerg" : "&n &a"; "",
+ "wunder" : "&n &a"; "",
+ "zeig" : "<Objekt-ID>"; "&aZeigt das Objekt (Langbeschreibung).",
+ "zitier" : "&n &a"; "Hier uebernimmt das Adverb ggf. die Stelle des "
+ +"zitierten, wenn dieser nicht anwesend ist (zitiere Ja /Jof)",
+ "zitter" : "&a"; "",
+ "zwinker" : "&n &a"; "&g",
+ "aechz" : "&a"; "",
+ "argl" : "&a"; "",
+ "atm" : "&a"; "&derleichtert",
+ "bewunder" : "&n! &a"; "",
+ "erbleich" : "&a"; "&g",
+ "fluch" : "&a"; "&g",
+ "grunz" : "&a"; "",
+ "kuschel" : "&n! &a"; "&g",
+ "rotfl" : "&a"; "",
+ "verfluch" : "{&n &a} | &t!"; "",
+ "kitzel" : "&n! &a"; "&g",
+ "nein" : "&a"; "&g",
+ "deut" : "&n! &a"; "&a",
+ "denk" : "&a | &t"; "&g",
+ "knuddel" : "&n! &a"; "&g&a",
+ "kratz" : "&a"; "",
+ "streck" : "&n &a [[die] zunge raus"; "&g",
+ "taetschel" : "&n! &a"; "&g",
+ "wuschel" : "&n! &a"; "",
+ "rassel" : "&a"; "&g&dfuerchterlich",
+ "heul" : "&a"; "&g&dschauerlich",
+ "kick" : "&n &a"; "&g&a",
+ "splash" : "&n! &a"; "&a",
+ "flame" : "&n! &a"; "&g&a",
+ "rknuddel" : "&n! &a"; "Nur fuer Ferngebrauch gedacht.",
+ "rwink" : "&n! &a"; "Nur fuer Ferngebrauch gedacht.",
+ "verb" : ""; "Listet alle derzeit moeglichen Verben auf.",
+ "adverb" : "[# | $ | {? <Abkuerzung>} | <Abkuerzung> [<Adverb>]]";
+ "Genauere Hilfe mit \"adverb ?\"",
+ ]);
+}
+
+/**
+ Gibt die Hilfe zu einem Verb oder die Verbenuebersicht zurueck
+ \param verb "string" Ein Verb dessen Hilfe angezeigt werden soll.
+ 0 oder "" Ueberblicksseite
+ \return String der an den Spieler ausgegeben werden kann.
+*/
+string Help(string verb) {
+ string out;
+ if (verb && verb!="")
+ return HelpVerb(verb);
+ out="";
+ out+=break_string("Standardverben:\n"+implode(SortIt(plrcmds), ", ")
+ +".", 78, 0, 1);
+ if (IS_WIZARD(this_player()))
+ out+="\n"+break_string("Magierverben:\n"+implode(SortIt(wizcmds), ", ")
+ +".", 78, 0, 1);
+ if (this_player()->ghost())
+ out+="\n"+break_string("Geisterverben:\n"+implode(SortIt(ghostcmds), ", ")
+ +".", 78, 0, 1);
+ out+=break_string("\nAdverbien koennen entweder in der Abkuerzung (bei "
+ +"bereits definierten Adverbien) oder mit \"/<Text>\" angegeben werden. "
+ +"Bei der zweiten Methode wird <Text> so, wie er angegeben wurde, an "
+ +"Stelle des Adverbs eingesetzt.", 78, 0, 1);
+ out+="\n"+break_string("Eine kurze Hilfe zu einem Verb bekommst du mit einem der "
+ +"Befehle \"<Verb> -?\", \"<Verb> /?\", \"<Verb> -h\", \"<Verb> /h\" oder "
+ +"\"<Verb> hilfe\". (Da war jemand wirklich fleissig).", 78);
+ return out+"\n"+break_string("Einige Befehle reagieren auch auf leicht "
+ +"unterschiedliche Schreibweise. Diese Befehlsliste kann sich mit Deinem "
+ +"Zustand aendern.", 78);
+}
+
+/**
+ Sortiert ein array of string alphabetisch.
+ \param arr Das zu sortierende Array
+ \return Sortierte Kopie des Arrays
+*/
+private string*
+SortIt(string *arr) {
+ return sort_array(arr,
+ function int (string x, string y){
+ return lower_case(x) > lower_case(y) ;
+ }
+ );
+}
+
+#define NO_MORE_E ({"cls","flipp","gruebel","grummel","guck","haetschel",\
+ "jubel","laechel","gib","runzel","schmunzel","schuettel","sniff",\
+ "streichel","tritt", "wackel","argl","rotfl","kuschel","kitzel","knuddel",\
+ "taetschel","wuschel","rassel","kick","splash","flame","verb","adverb",\
+ "nein","brummel","mopper",})
+
+/**
+ Gibt die Hilfe zu einem Verb
+ \param v Ein Verb dessen Hilfe angezeigt werden soll.
+ \return Hilfe zum Verb in Form eines Strings
+*/
+private string
+HelpVerb(string v) {
+ string h,t;
+ if (member(m_indices(help),v)<0)
+ return "Dazu ist keine Hilfe vorhanden.\n";
+ h="Syntax:\n* "+v;
+ if (member(NO_MORE_E, v)<0)
+ h+="e";
+ h+=" ";
+ t=help[v,0];
+ t=implode(explode(t,"&t!"), "<Text>");
+ t=implode(explode(t,"&n!"), "<Name>");
+ t=implode(explode(t,"&t"), "[<Text>]");
+ t=implode(explode(t,"&n"), "[<Name>]");
+ t=implode(explode(t,"&a"), "[<Adverb>]");
+ h+=t;
+ t=help[v,1];
+ if (t!="") {
+ h+="\nBemerkungen:";
+ if ((explode(t,"&"))[0]!="")
+ t="\n"+break_string((explode(t,"&"))[0], 76, "* ", 1)[0..<2]
+ +"&"+implode((explode(t,"&"))[1..<1],"&");
+ t=implode(explode(t,"&g"), "\n* Verhaelt sich bei Geistern anders "
+ +"als sonst.");
+ t=implode(explode(t,"&a"), "\n* Bei diesem Verb geht alle(n) als "
+ +"Zielangabe.");
+ if (strstr(t,"&d")>=0) {
+ t=implode(explode(t,"&d"), "\n* Standardadverb ist ");
+ t+=".";
+ }
+ }
+ return break_string(h+t, 78, 2, 1);
+}
diff --git a/std/player/travel.c b/std/player/travel.c
new file mode 100644
index 0000000..3e07c0a
--- /dev/null
+++ b/std/player/travel.c
@@ -0,0 +1,506 @@
+
+/* 'reise' handling
+ *
+ * Ueberarbeitete und
+ * erweiterte Version: Tilly@MorgenGrauen, 10.01.02
+ * Basierend auf : base.c@SilberLand,
+ * Revision 3.55, Woody@SilberLand, 11.05.99
+ */
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player.h>
+#include <living/moving.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <config.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <living.h>
+#include <attributes.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <combat.h>
+#include <transport.h>
+
+void create(){}
+
+private string _traveller(string *s)
+{
+ switch (sizeof(s))
+ {
+ case 1 : return s[0];
+ case 2 : return s[0]+" oder "+s[1];
+ }
+ return efun::implode(s[0..<2],", ")+" oder "+s[<1];
+}
+
+static int reise(string str)
+{
+ mixed t, ship, dstr;
+ string mit, nach, s1, s2;
+ int i;
+
+ _notify_fail("Syntax: reise mit <transportmittel> nach <zielort>\n\n"
+ "Weitere Syntaxen moeglich, bitte 'hilfe reise' lesen.\n");
+
+ t = QueryProp(P_TRAVEL_INFO);
+
+ if (!pointerp(t) || (sizeof(t) < 4) || !objectp(t[0]) || !objectp(t[1]))
+ {
+ SetProp(P_TRAVEL_INFO, t = 0);
+ }
+/* * * *
+ * REISE
+ * * * */
+ if (!str)
+ {
+ if (!t)
+ {
+ write("Du hast keine Reiseroute festgelegt.\n");
+ }
+ else if (t[0] == environment())
+ {
+ if (environment()->id("Transporter"))
+ {
+ write(sprintf("Du reist mit %s%s.\n",environment()->name(WEM,1),
+ t[2]?" "+t[3]:""));
+ }
+ else
+ {
+ write(sprintf("Du wirst mit %s%s reisen.\n",t[1]->name(WEM,1),
+ t[2]?" "+t[3]:""));
+ }
+ }
+ else
+ {
+ write(sprintf("Deine letzte Route (mit %s%s) ist hier nicht wirksam.\n",
+ t[1]->name(WEM,1),
+ t[2]?" "+t[3]:""));
+ }
+ return 1;
+ }
+
+ str = lower_case( _unparsed_args() );
+/* * * * * * *
+ * REISE ROUTE
+ * * * * * * */
+ if (str == "route")
+ {
+ string *harbours;
+
+ if (environment()->id("Transporter"))
+ {
+ if (environment()->QueryProp(P_NO_TRAVELING))
+ {
+ write(break_string(
+ capitalize(environment()->name(WER,1))+" hat gar keine "
+ "Reiseroute. Wo Dich das wohl hinfuehrt?",78));
+ return 1;
+ }
+
+ harbours = environment()->QueryHarbours(1);
+
+ if (!sizeof(harbours) || !stringp(harbours[0]))
+ {
+ write("Die Reiseroute "+environment()->name(WESSEN,1)+" ist "
+ +"leider nicht bekannt.\n");
+ }
+ else
+ {
+ write(break_string(capitalize(efun::implode(harbours," - "))+".",78,
+ "Reiseroute "+environment()->name(WESSEN,1)+": ",BS_INDENT_ONCE));
+ }
+ return 1;
+ }
+ if (environment()->QueryProp(P_NO_TRAVELING))
+ {
+ write(break_string("Hier kannst Du leider keine Reiseroute waehlen. "
+ "Was nicht heisst, dass hier kein Transportmittel "
+ "verkehrt.",78));
+ return 1;
+ }
+ if (!pointerp(ship = TRAVELD->HasTransporter(environment())))
+ {
+ _notify_fail("Hier verkehrt kein Transportmittel.\n");
+ return 0;
+ }
+
+ write("Hier verkehren folgende Transportmittel \n"
+ "--------------------------------------- \n");
+
+ for (i = 0; i < sizeof(ship); i++)
+ {
+ if (!ship[i]->Query(P_SHORT)) continue;
+
+ harbours = ship[i]->QueryHarbours(1);
+
+ if (sizeof(harbours) && stringp(harbours[0]))
+ {
+ write(break_string(efun::implode(harbours," - ")+".",78,
+ ship[i]->Query(P_SHORT)+": ",BS_INDENT_ONCE));
+ }
+ else
+ {
+ write(ship[i]->Query(P_SHORT)+": Route unbekannt.\n");
+ }
+ }
+ return 1;
+ }
+/* * * * * * *
+ * REISE AUS
+ * REISE NICHT
+ * * * * * * */
+ if (member((["aus","nicht"]),str))
+ {
+ if (!t)
+ {
+ write("Du hattest keine Reiseroute eingestellt.\n");
+ }
+ else
+ {
+ write("Du loeschst Deine Reiseroute.\n");
+ }
+ SetProp(P_TRAVEL_INFO, 0);
+ return 1;
+ }
+
+ str = regreplace(str,"\\<(zu|zum|zur|ins|ans)\\>","nach",0);
+/* * * * * * * * *
+ * REISE MIT NACH
+ * REISE NACH MIT
+ * * * * * * * * */
+ if ((sscanf(str, "mit %s nach %s", mit, nach) == 2) ||
+ (sscanf(str, "nach %s mit %s", nach, mit) == 2))
+ {
+ _notify_fail("Hier kannst Du leider keine Reiseroute waehlen.\n");
+
+ if (environment()->QueryProp(P_NO_TRAVELING))
+ {
+ return 0;
+ }
+ if (!sizeof(nach))
+ {
+ _notify_fail("Syntax: reise mit <transportmittel> nach <zielort>\n"
+ " reise nach <zielort> mit <transportmittel>\n");
+ return 0;
+ }
+ if (environment()->id("Transporter"))
+ {
+ if (environment()->id(mit))
+ {
+ command("reise nach "+nach);
+ return 1;
+ }
+ else
+ {
+ _notify_fail("Beende erstmal Deine aktuelle Reise mit "+
+ environment()->name(WEM,1)+".\n");
+ return 0;
+ }
+ }
+ if (!pointerp(ship = TRAVELD->HasTransporter(environment(), mit)))
+ {
+ _notify_fail("So ein Transportmittel verkehrt hier nicht.\n");
+ return 0;
+ }
+ for (i = sizeof(ship) -1 ; i >= 0; i--)
+ if (!ship[i]->HasRoute(nach))
+ {
+ ship[i] = 0;
+ }
+
+ ship -= ({0});
+
+ if (pointerp(t) && objectp(t[1]) && (member(ship,t[1]) != -1))
+ {
+ ship = ({ t[1] });
+ }
+ if (sizeof(ship) > 1)
+ {
+ if (object_name(environment()) == ship[0]->HasRoute(nach)[0])
+ {
+ _notify_fail("Aber da bist Du doch bereits.\n");
+ return 0;
+ }
+ write("Dorthin kannst Du mit "+CountUp(map_objects(ship,"name",WEM))
+ +"reisen.\n");
+
+
+ dstr = filter( filter_objects(ship,"short"), lambda( ({'x}),
+ ({ #'==, ({#'environment, 'x}), environment() }) ) );
+
+ if (sizeof(dstr))
+ {
+ ship = dstr[0];
+ }
+ else
+ {
+ ship = ship[0];
+ }
+
+ dstr = ship->HasRoute(nach);
+
+ write(sprintf("Du entscheidest Dich fuer %s und reist %s.\n",
+ ship->name(WEN,1),dstr[1]));
+ }
+ else if (sizeof(ship) < 1)
+ {
+ _notify_fail("Nach '"+capitalize(nach)+"' kann Dich das angegebene "
+ +"Transportmittel leider nicht bringen.\n");
+ return 0;
+ }
+ else
+ {
+ ship = ship[0];
+ dstr = ship->HasRoute(nach);
+
+ if (object_name(environment()) == dstr[0])
+ {
+ _notify_fail("Aber da bist Du doch bereits.\n");
+ return 0;
+ }
+ if (t && stringp(t[2]))
+ {
+ if (t[2] == dstr[0])
+ {
+ _notify_fail("Aber das tust Du doch bereits.\n");
+ return 0;
+ }
+ }
+ write(sprintf("Ok, Du reist nun mit %s %s.\n",
+ ship->name(WEM,1),dstr[1]));
+ }
+ if (environment(ship)==environment() && ship->short())
+ {
+ ship->Enter(this_object());
+ }
+ SetProp(P_TRAVEL_INFO, ({ environment(), ship, dstr[0], dstr[1] }) );
+ return 1;
+ }
+/* * * * * * *
+ * REISE NACH
+ * * * * * * */
+ if (sscanf(str,"nach %s",nach))
+ {
+ _notify_fail("Hier kannst Du leider keine Reiseroute waehlen.\n");
+
+ if (environment()->QueryProp(P_NO_TRAVELING))
+ {
+ return 0;
+ }
+ if (environment()->id("Transporter"))
+ {
+ if (!dstr = environment()->HasRoute(nach))
+ {
+ _notify_fail("Dorthin kann Dich "+environment()->name(WER,1)+
+ " leider nicht bringen.\n");
+ return 0;
+ }
+ if (t && stringp(t[2]))
+ {
+ if (t[2] == dstr[0])
+ {
+ _notify_fail("Aber das tust Du doch bereits.\n");
+ return 0;
+ }
+ }
+ write(sprintf("Ok, Du reist jetzt mit %s %s.\n",
+ environment()->name(WEM,1),dstr[1]));
+
+ if (IS_WIZARD(this_object()))
+ {
+ write("Als Magier nimmst Du natuerlich die Abkuerzung.\n");
+ move(dstr[0],M_NOCHECK);
+ return 1;
+ }
+ SetProp(P_TRAVEL_INFO,({ environment(),
+ environment(),
+ dstr[0],
+ dstr[1] }) );
+
+ if (object_name(environment(ship = environment())) == dstr[0] &&
+ ship->short())
+ {
+ environment()->Leave(this_object());
+
+ if (environment() != ship)
+ {
+ SetProp(P_TRAVEL_INFO, 0);
+ }
+ }
+ return 1;
+ }
+ if (!pointerp(ship = TRAVELD->HasTransporter(environment())))
+ {
+ _notify_fail("Von hier aus kannst Du nicht reisen.\n");
+ return 0;
+ }
+ for (i = sizeof(ship) - 1; i >= 0; i--)
+ if (!ship[i]->HasRoute(nach))
+ {
+ ship[i] = 0;
+ }
+
+ ship -= ({ 0 });
+
+ if (pointerp(t) && objectp(t[1]) && (member(ship,t[1]) != -1))
+ {
+ ship = ({ t[1] });
+ }
+ if (sizeof(ship) > 1)
+ {
+ if (object_name(environment()) == ship[0]->HasRoute(nach)[0])
+ {
+ _notify_fail("Aber da bist Du doch bereits.\n");
+ return 0;
+ }
+
+ write(break_string("Dahin kannst Du mit "
+ +_traveller(map_objects(ship, "name", WEM))+" gelangen.",78));
+
+ dstr = filter(filter_objects(ship,"short"),lambda( ({'x}),
+ ({ #'==, ({#'environment, 'x}), environment() }) ) );
+
+ if (sizeof(dstr))
+ {
+ ship = dstr[0];
+ }
+ else
+ {
+ ship = ship[0];
+ }
+
+ dstr = ship->HasRoute(nach);
+
+ write(sprintf("Du waehlst %s und reist %s.\n",ship->name(WEN,1),
+ dstr[1]));
+ }
+ else if (sizeof(ship) < 1)
+ {
+ _notify_fail("Nach '"+capitalize(nach)+"' kann Dich leider keines der "
+ +"hier verkehrenden Transportmittel bringen.\n");
+ return 0;
+ }
+ else
+ {
+ ship = ship[0];
+ dstr = ship->HasRoute(nach);
+
+ if (object_name(environment()) == dstr[0])
+ {
+ _notify_fail("Aber da bist Du ja bereits.\n");
+ return 0;
+ }
+ else if (t && stringp(t[2]))
+ {
+ if (t[2] == dstr[0])
+ {
+ _notify_fail("Aber das tust Du doch bereits.\n");
+ return 0;
+ }
+ }
+ write(sprintf("Ok, Du reist nun mit %s %s.\n",ship->name(WEM),
+ dstr[1]));
+ }
+ if (IS_WIZARD(this_object()))
+ {
+ write("Als Magier nimmst Du natuerlich die Abkuerzung.\n");
+ move(dstr[0],M_NOCHECK);
+ return 1;
+ }
+ if (environment(ship)==environment() && ship->short())
+ {
+ ship->Enter(this_object());
+ }
+ SetProp(P_TRAVEL_INFO, ({ environment(), ship, dstr[0], dstr[1] }) );
+ return 1;
+ }
+/* * * * * *
+ * REISE MIT
+ * * * * * */
+ if (sscanf(str, "mit %s", mit))
+ {
+ _notify_fail("Hier kannst Du leider keine Reiseroute waehlen.\n");
+
+ if (environment()->QueryProp(P_NO_TRAVELING))
+ {
+ return 0;
+ }
+ if (environment()->id("Transporter"))
+ {
+ if (environment()->id(mit))
+ {
+ _notify_fail("Aber das tust Du doch bereits.\n");
+ return 0;
+ }
+ else
+ {
+ _notify_fail("Beende erstmal Deine aktuelle Reise mit "+
+ environment()->name(WEM,1)+".\n");
+ return 0;
+ }
+ }
+ if (t && objectp(t[1]) && t[1]->id(mit) && t[0] == environment())
+ {
+ _notify_fail("Aber das tust Du doch bereits.\n");
+ return 0;
+ }
+ if (!pointerp(ship = TRAVELD->HasTransporter(environment(),mit)))
+ {
+ _notify_fail("So ein Transportmittel verkehrt hier nicht.\n");
+ return 0;
+ }
+ if (sizeof(ship) > 1)
+ {
+ write("'"+capitalize(mit)+"' koennte "
+ +_traveller(map_objects(ship,"name",WER))+" sein.\n");
+
+ dstr = filter(filter_objects(ship,"short"),lambda( ({'x}),
+ ({ #'==, ({#'environment, 'x}), environment() }) ) );
+
+ if (sizeof(dstr))
+ {
+ ship = dstr[0];
+ }
+ else
+ {
+ ship = ship[0];
+ }
+ write(sprintf("Du waehlst %s.\n", ship->name(WEN,1)));
+ }
+ else if (sizeof(ship) < 1)
+ {
+ notify_fail("So ein Transportmittel verkehrt hier nicht.\n");
+ return 0;
+ }
+ else
+ {
+ ship = ship[0];
+ write(sprintf("Du reist nun mit %s.\n",ship->name(WEM,1)));
+ }
+ if (environment(ship)==environment() && ship->short())
+ {
+ ship->Enter(this_object());
+ }
+ if (pointerp(t) && stringp(t[2]) && stringp(t[3]) &&
+ member(ship->QueryHarbours(),t[2]) != -1)
+ {
+ write("Du behaeltst Dein bisheriges Reiseziel ("+t[3]+") bei.\n");
+ SetProp(P_TRAVEL_INFO, ({ environment(), ship, t[2], t[3] }) );
+ }
+ else
+ {
+ SetProp(P_TRAVEL_INFO, ({ environment(), ship, 0, 0 }) );
+ }
+ return 1;
+ }
+ return 0;
+}
+
diff --git a/std/player/util.c b/std/player/util.c
new file mode 100644
index 0000000..a6c8fa9
--- /dev/null
+++ b/std/player/util.c
@@ -0,0 +1,111 @@
+// MorgenGrauen MUDlib
+//
+// player/util. -- Utilities
+//
+// $Id: util.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include "/sys/player/util.h"
+#include "/sys/thing/properties.h"
+
+public void ShowPropList(string *props)
+{
+ int i,j;
+
+ j=sizeof(props);
+
+ for ( i=0; i<j ; i++)
+ {
+ write("*"+props[i]+": ");
+ PrettyDump(QueryProp(props[i]));
+ write("\n");
+ }
+}
+
+static void PrettyDump(mixed x)
+{
+ if (pointerp(x))
+ {
+ DumpArray(x);
+ }
+ else if (mappingp(x))
+ {
+ DumpMapping(x);
+ }
+ else if (objectp(x))
+ {
+ write ("OBJ("+object_name(x)+")");
+ }
+ else if (stringp(x))
+ {
+ write("\""+x+"\"");
+ }
+ else
+ {
+ write (x);
+ }
+}
+
+static void DumpArray(mixed *x)
+{
+ int i,j;
+
+ write ("({ ");
+ if ( (j=sizeof(x))>0 )
+ {
+ for ( i=0 ; i<(j-1) ; i++)
+ {
+ PrettyDump(x[i]);
+ write(", ");
+ }
+ PrettyDump(x[i]);
+ write(" ");
+ }
+ write ("})");
+}
+
+static void DumpMapping(mapping x)
+{
+ int i, c, s;
+ mixed *ind;
+
+ write("([ ");
+
+ if ( (c=sizeof(ind=m_indices(x)))<1 )
+ {
+ write(" ])");
+ return;
+ }
+
+ s=get_type_info(x,1);
+
+ DumpKeyValPair(x, ind[0], s);
+ for ( i=1 ; i<c ; i++ )
+ {
+ write(", ");
+ DumpKeyValPair(x, ind[i], s);
+ }
+ write(" ])");
+}
+
+// Lacht nicht ueber den Namen!!! -Boing
+// Nein, ueber den Namen lache ich nicht ... -Paracelsus
+static void DumpKeyValPair(mapping x, mixed key, int size)
+{ int j, vc;
+
+ PrettyDump(key);
+ write(" : ");
+ PrettyDump(x[key,0]);
+
+ for ( j=1; j<size; j++)
+ {
+ write("; ");
+ PrettyDump(x[key, j]);
+ }
+}
diff --git a/std/player/viewcmd.c b/std/player/viewcmd.c
new file mode 100644
index 0000000..5d7e618
--- /dev/null
+++ b/std/player/viewcmd.c
@@ -0,0 +1,754 @@
+// MorgenGrauen MUDlib
+//
+// player/viewcmd.c -- player view command handling
+//
+// $Id: viewcmd.c 9548 2016-04-17 19:28:22Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include "/sys/thing/properties.h"
+#include "/sys/living/put_and_get.h"
+#include "/sys/living/description.h"
+
+#include <container.h>
+#include <player.h>
+#include <properties.h>
+#include <rooms.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <moving.h>
+#include <new_skills.h>
+#include <ansi.h>
+
+#include <sys_debug.h>
+
+varargs mixed More(string str, int fflag, string returnto);
+
+void create()
+{
+ Set(P_BRIEF, SAVE, F_MODE);
+ Set(P_BLIND, SAVE, F_MODE);
+}
+
+int _toggle_brief()
+{
+ int brief;
+
+ if (query_verb()=="kurz")
+ brief=1;
+ else if (query_verb()=="ultrakurz")
+ brief=2;
+ else brief=0;
+ SetProp(P_BRIEF, brief);
+ write("Du bist nun im \""+
+ (brief?(brief==1?"Kurz":"Ultrakurz"):"Lang")+"\"modus.\n");
+ return 1;
+}
+
+private int sortinv(mixed a, mixed b) { return a[0] > b[0]; }
+
+private string collectinv(mixed obj)
+{
+ if(obj[0]=="") return 0;
+ return (obj[2] ? " " : "")
+ + obj[0]
+ + (obj[1] > 1 ? " ("+obj[1]+")" : "");
+}
+
+#define I_AUTOLOAD 1
+#define I_KEEP 4
+#define I_FORMATTED 16
+#define I_ARMOUR 64
+#define I_SORT 256
+#define I_WEAPON 1024
+#define I_FORCE_SORT 4096
+#define I_NO_TABLE 16384
+
+private string getflags(string arg, int flags)
+{
+ int no, i;
+ if(sizeof(arg) < 2) return 0;
+ no = (arg[0] == '-');
+
+ for(i = 1; i < sizeof(arg); i++)
+ {
+ switch(arg[i])
+ {
+ case 'a': flags |= I_AUTOLOAD << no; break;
+ case 'b': flags |= I_KEEP << no; break;
+ case 'f': flags |= I_FORMATTED << no; break;
+ case 'r': flags |= I_ARMOUR << no; break;
+ case 's': flags |= I_SORT << no; break;
+ case 'w': flags |= I_WEAPON << no; break;
+ case 'v': flags |= (I_ARMOUR | I_WEAPON) << !no; break;
+ case '1': flags |= I_NO_TABLE; break;
+ // Die Option macht nur Aerger und kommentiert ist sie eh nicht.
+ // Wer das dringend braucht, soll Wargons Schiebepuzzle benutzen.
+ //
+ // Tiamak, 15.10.2000
+ // case 'S': flags |= I_FORCE_SORT << no; break;
+ default : return arg[i..i]; // wird ausgegeben an Spieler als unbekannt.
+ }
+ }
+ return 0;
+}
+
+static int _check_keep(object ob)
+{
+ return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
+}
+
+int _inventory(string str)
+{
+ mixed *args, output;
+ int ansi, i, flags, minv;
+ mixed inventory, weapons, armours, misc;
+ string format;
+
+ if(CannotSee()) return 1;
+
+ if((str = _unparsed_args()) && str!="")
+ {
+ string error;
+ error = "Benutzung: i[nventar] [-/+1abfrsvw]\n";
+ args = regexp(regexplode(str, "[-+][1abfrswv][1abfrswv]*"),
+ "[-+][1abfrswv][1abfrswv]*");
+ if(!sizeof(args)) return (_notify_fail(error), 0);
+ if(sizeof(args = map(args, #'getflags/*'*/, &flags) - ({ 0 })))
+ {
+ printf("%s: Unbekanntes Argument.\n"+error, implode(args, ", "));
+ return 1;
+ }
+ }
+ // Fuer Spieler gehen nur sichtbare Objekte in den Algorithmus
+ if (IS_LEARNING(ME))
+ inventory = all_inventory(ME);
+ else
+ inventory = filter_objects(all_inventory(ME), "short");
+
+ ansi = member(({"vt100", "ansi"}), QueryProp(P_TTY)) != -1;
+ minv = 1 | (flags & (I_FORMATTED | (I_FORMATTED << 1)) ? 2 : 0);
+ format = (flags & I_NO_TABLE) ? "=" : "#";
+
+// if(flags & (I_FORCE_SORT | I_FORCE_SORT << 1))
+// {
+// closure sf;
+// sf = flags & I_FORCE_SORT ? #'> : #'<;
+// s = sort_array(s, lambda(({'a, 'b}),
+// ({#'funcall, sf,
+// ({#'||,({#'call_other,'a,"short"}),""}),
+// ({#'||,({#'call_other,'b,"short"}),""})})));
+// map_objects(s, "move", this_object());
+// s = all_inventory(ME);
+// }
+
+ if (flags & I_AUTOLOAD)
+ inventory = filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
+ else if (flags & (I_AUTOLOAD << 1))
+ inventory -= filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
+
+ if(flags & I_KEEP)
+ inventory = filter(inventory,#'_check_keep);
+ else if(flags & (I_KEEP << 1))
+ inventory -= filter(inventory,#'_check_keep);
+
+ armours = filter_objects(inventory, "QueryProp", P_ARMOUR_TYPE);
+ // Kleidung dazu addieren, vorher die erkannten Ruestungen abziehen, die
+ // muessen nicht nochmal durchiteriert werden.
+ armours += filter_objects(inventory-armours, "IsClothing");
+ // Ruestungen werden hier nicht abgezogen, weil es Kram gibt, welche sowohl
+ // Ruestung als auch Waffe ist.
+ weapons = filter_objects(inventory, "QueryProp", P_WEAPON_TYPE);
+ misc = inventory - weapons - armours; // rest ;-)
+
+ if(flags & I_WEAPON)
+ {
+ inventory = weapons; misc = ({});
+ if(!(flags & (I_ARMOUR))) armours = ({});
+ }
+ if(flags & I_ARMOUR)
+ {
+ inventory = armours; misc = ({});
+ if(!(flags & I_WEAPON)) weapons = ({});
+ }
+ if(flags & (I_WEAPON << 1)) { weapons = ({}); inventory = armours + misc; }
+ if(flags & (I_ARMOUR << 1)) { armours = ({}); inventory = weapons + misc; }
+
+ output = "";
+ if(flags & (I_FORMATTED | (I_FORMATTED << 1)))
+ {
+ inventory = make_invlist(this_player(), inventory, minv);
+ if(flags & (I_SORT | (I_SORT << 1)))
+ inventory = sort_array(inventory, #'sortinv/*'*/);
+ output += sprintf("%"+format+"-78s\n",
+ implode(map(inventory,#'collectinv/*'*/),"\n"));
+ }
+ else
+ {
+ if(weapons && sizeof(weapons))
+ {
+ weapons = make_invlist(this_player(), weapons, minv);
+ if(flags & (I_SORT | (I_SORT << 1)))
+ weapons = sort_array(weapons, #'sortinv/*'*/);
+ output += (ansi?ANSI_BOLD:"") + "Waffen:" + (ansi?ANSI_NORMAL:"")+"\n"
+ + sprintf("%"+format+"-78s\n",
+ implode(map(weapons, #'collectinv/*'*/), "\n"));
+ }
+ if(armours && sizeof(armours))
+ {
+ armours = make_invlist(this_player(), armours, minv);
+ if(flags & (I_SORT | (I_SORT << 1)))
+ armours = sort_array(armours, #'sortinv/*'*/);
+ output += (ansi?ANSI_BOLD:"")
+ + "Kleidung & Ruestungen:" + (ansi?ANSI_NORMAL:"")+"\n"
+ + sprintf("%"+format+"-78s\n",
+ implode(map(armours, #'collectinv/*'*/), "\n"));
+ }
+ if(misc && sizeof(misc))
+ {
+ misc = make_invlist(this_player(), misc, minv);
+ if(flags & (I_SORT | (I_SORT << 1)))
+ misc = sort_array(misc, #'sortinv/*'*/);
+ output += (ansi?ANSI_BOLD:"") + "Verschiedenes:" + (ansi?ANSI_NORMAL:"")+"\n"
+ + sprintf("%"+format+"-78s\n",
+ implode(map(misc, #'collectinv/*'*/), "\n"));
+ }
+ }
+
+ // Spielerwunsch: 'inventar' sollte doch das Bezugsobjekt auf den Spieler
+ // aendern.
+ SetProp(P_REFERENCE_OBJECT, this_object());
+
+ if (output=="")
+ output += (ansi?ANSI_BOLD:"")+"Die Liste ist leer."+(ansi?ANSI_NORMAL:"");
+ More(output);
+ return 1;
+}
+
+private nosave int exa_cnt;
+private nosave int exa_time;
+private nosave string *exa;
+
+varargs int _examine(string str, int mode)
+{
+ object base, *objs, env;
+ string what, detail, parent, out, error;
+ int i, size, done;
+
+ if(CannotSee()) return 1;
+
+ _notify_fail("Was willst Du denn untersuchen?\n");
+ if (!str) return 0;
+
+ if (member(({"boden","decke","wand","waende"}),old_explode(str," ")[0]) == -1) {
+ exa_cnt -= (time() - exa_time)/2;
+ exa_time = time();
+ exa_cnt++;
+ if (!exa)
+ exa = ({ str });
+ else
+ exa += ({ str });
+ if (exa_cnt > 10) {
+ log_file("ARCH/LOOK",
+ sprintf("%s: %s in %s\n%@O\n",dtime(time()),getuid(this_object()),
+ environment() ? object_name(environment()) : "???",exa), 150000);
+ exa_cnt = 0;
+ exa = ({});
+ }
+ else if (exa_cnt < 0) {
+ exa_cnt = 0;
+ exa = ({});
+ }
+ }
+ // do we look at an object in our environment ?
+ if (sscanf(str,"%s in raum", what) || sscanf(str,"%s im raum", what))
+ base = environment();
+ // is the object inside of me (inventory)
+ else if (sscanf(str,"%s in mir", what) || sscanf(str,"%s in dir", what))
+ base = this_object();
+ else {
+ what = str;
+ // get the last object we looked at
+ base = QueryProp(P_REFERENCE_OBJECT);
+
+ // if a reference object exists, test for its existance in the room
+ // or in our inventory
+ if (objectp(base))
+ {
+ if (base == environment() || base==this_object())
+ {
+ // Umgebung oder Spieler selber sind als Bezugsobjekt immer in
+ // Ordnung, nichts machen.
+ }
+ // Das Referenzobjekt darf nicht unsichtbar sein.
+ else if (!base->short())
+ base = 0;
+ else if(member(deep_inventory(environment()), base) != -1)
+ {
+ foreach(env : all_environment(base)) {
+ // Ist eine Umgebung Living oder intransparenter Container oder
+ // unsichtbar?
+ if (living(env) || !env->QueryProp(P_TRANSPARENT)
+ || !env->short())
+ {
+ // in dem Fall ist ende, aber wenn das gefundene Env nicht
+ // dieses Living selber oder sein Env ist, wird das
+ // Referenzobjekt zusaetzlich genullt.
+ if (env != this_object() && env != environment())
+ base = 0;
+ break;
+ }
+ }
+ }
+ else
+ base = 0; // nicht im Raum oder Inventory
+ }
+ }
+
+ // scan input if we want a specific object to look at
+ if(sscanf(what, "%s an %s", detail, parent) == 2 ||
+ sscanf(what, "%s am %s", detail, parent) == 2 ||
+ sscanf(what, "%s in %s", detail, parent) == 2 ||
+ sscanf(what, "%s im %s", detail, parent) == 2)
+ {
+ // if an ref object exists get its inventory. (Oben wurde sichergestellt,
+ // dass das Referenzobjekt einsehbar ist)
+ if(base)
+ objs = base->locate_objects(parent, 1) || ({});
+ else {
+ // else get our inv and env
+ objs = environment()->locate_objects(parent, 1)
+ + locate_objects(parent, 1);
+ }
+ objs = filter_objects(objs, "short"); // nur sichtbare...
+ if(sizeof(objs) > 1)
+ return (notify_fail("Es gibt mehr als eine(n) "+capitalize(parent)+".\n"), 0);
+ else
+ {
+ if (sizeof(objs))
+ base = objs[0];
+ else
+ return (notify_fail("Hier ist kein(e) "+capitalize(parent)+".\n"), 0);
+ }
+ objs = 0;
+ }
+ else detail = what;
+
+ int base_was_env = 1;
+ do {
+ // if a base exists get its inventory, else get our inv and env
+ if (base)
+ {
+ if (base == this_object() || base == environment() ||
+ (base->QueryProp(P_TRANSPARENT) && !living(base)))
+ {
+ // ich kann in base reingucken...
+ objs = base->locate_objects(detail, 1) || ({});
+ }
+ else
+ {
+ // Referenzobjekt da, aber nicht reinguckbar. base aber nicht nullen,
+ // denn es ist ja noch gueltig fuer Detailsuchen an base...
+ objs = ({});
+ }
+ }
+ else
+ {
+ objs = environment()->locate_objects(detail, 1)
+ + locate_objects(detail, 1);
+ base = environment();
+ }
+ objs = filter_objects(objs, "short"); // nur sichtbare...
+
+ if(!sizeof(objs))
+ {
+ // wenn keine Objekte gefunden wurden, wird nach Details gesucht...
+ if((out = base->GetDetail(detail, QueryProp(P_REAL_RACE),SENSE_VIEW)) ||
+ (out = base->GetDoorDesc(detail)))
+ {
+ SetProp(P_REFERENCE_OBJECT, base);
+ return (write(out), 1);
+ }
+ else
+ {
+ // wenn auch keine Details gefunden, dann schauen, ob Ende ist
+ // (base==env) oder wir evtl. noch im env suchen koennen.
+ if (base == environment())
+ {
+ if (base_was_env) {
+ // in diesem Fall war das Env das Bezugsobjekt - daher wegwerfen.
+ SetProp(P_REFERENCE_OBJECT, 0);
+ _notify_fail("Sowas siehst Du da nicht!\n");
+ }
+ else {
+ _notify_fail("Sowas siehst Du auch da nicht!\n");
+ // in diesem Fall war nicht das Env das Bezugsobjekt - es soll
+ // behalten und nicht geloescht werden.
+ }
+ return 0;
+ }
+ else {
+ base_was_env=0;
+ write(break_string("Du findest an "+base->name(WEM)
+ +" kein \"" + capitalize(detail) + "\"."
+ " Dein Blick wendet sich der Umgebung zu.",78));
+ base = 0;
+ }
+ }
+ }
+ else // Objekte gefunden!
+ done = 1;
+ } while(!done);
+
+ // Es muss min. ein (sichtbares) Objekt geben, sonst waere man hier nicht
+ // hingekommen.
+ object ob = objs[0];
+ SetProp(P_REFERENCE_OBJECT, ob);
+ tell_object(ME, ob->long(mode));
+ return 1;
+}
+
+varargs int _sense_exa(string str)
+{
+ object base, *objs, env;
+ string what, detail, parent, out, error;
+ int sense;
+
+ if(member(({"riech","rieche","schnupper","schnuppere"}),query_verb())!=-1)
+ {
+ _notify_fail("Du kannst nichts Besonderes riechen.\n");
+ sense = SENSE_SMELL;
+ }
+ else if(member(({"lausche","lausch","hoer","hoere"}),query_verb())!=-1)
+ {
+ if(QueryProp(P_DEAF))
+ return notify_fail("Du bist taub!\n"), 0;
+
+ _notify_fail("Du kannst nichts Besonderes hoeren.\n");
+ sense = SENSE_SOUND;
+ }
+ else if(member(({"taste","beruehre","beruehr"}),query_verb())!=-1)
+ {
+ sense = SENSE_TOUCH;
+ // ein "ab" ganz am Ende von str wird abgeschnitten, es soll sowohl "taste
+ // x ab" als auch "taste x" funktionieren.
+ if (str) {
+ _notify_fail("Sowas kannst Du hier nicht ertasten!\n");
+ string *tmp = explode(str," ");
+ if (sizeof(tmp) > 1 && tmp[<1] == "ab")
+ str = implode(tmp[0..<2], " ");
+ }
+ else
+ _notify_fail("Was willst Du denn abtasten?\n");
+ }
+ else if (member(({"lies","lese","les"}), query_verb()) > -1)
+ {
+ _notify_fail("Was willst Du lesen?\n");
+ if ( !str ) // Kein SENSE_DEFAULT zulassen.
+ return 0;
+ if (this_object()->CannotSee()) {
+ notify_fail("Du kannst nichts sehen!\n");
+ return 0;
+ }
+ sense = SENSE_READ;
+ }
+
+ if (!str) {
+ if(!detail =
+ environment()->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),sense))
+ return 0;
+ write(detail);
+ return 1;
+ }
+ else if(sscanf(str,"an %s",what)==1)
+ str=what;
+
+ // do we look at an object in our environment ?
+ if (sscanf(str,"%s in raum", what) || sscanf(str,"%s im raum", what))
+ base = environment();
+ // is the object inside of me (inventory)
+ else if (sscanf(str,"%s in mir", what) || sscanf(str,"%s in dir", what))
+ base = this_object();
+ else {
+ what = str;
+ // get the last object we looked at
+ base = QueryProp(P_REFERENCE_OBJECT);
+
+ // if a reference object exists, test for its existance in the room
+ // or in our inventory
+ if (objectp(base))
+ {
+ if (base == environment() || base==this_object())
+ {
+ // Umgebung oder Spieler selber sind als Bezugsobjekt immer in
+ // Ordnung, nichts machen.
+ }
+ // Das Referenzobjekt darf nicht unsichtbar sein.
+ else if (!base->short())
+ base = 0;
+ else if(member(deep_inventory(environment()), base) != -1)
+ {
+ foreach(env : all_environment(base)) {
+ // Ist eine Umgebung Living oder intransparenter Container oder
+ // unsichtbar?
+ if (living(env) || !env->QueryProp(P_TRANSPARENT)
+ || !env->short())
+ {
+ // in dem Fall ist ende, aber wenn das gefundene Env nicht
+ // dieses Living selber oder sein Env ist, wird das
+ // Referenzobjekt zusaetzlich genullt.
+ if (env != this_object() && env != environment())
+ base = 0;
+ break;
+ }
+ }
+ }
+ else
+ base = 0; // nicht im Raum oder Inventory
+ }
+ }
+
+ // scan input if we want a specific object to look at
+ if(sscanf(what, "%s an %s", detail, parent) == 2 ||
+ sscanf(what, "%s am %s", detail, parent) == 2 ||
+ sscanf(what, "%s in %s", detail, parent) == 2 ||
+ sscanf(what, "%s im %s", detail, parent) == 2)
+ {
+ // if an ref object exists get its inventory. (Oben wurde sichergestellt,
+ // dass das Referenzobjekt einsehbar ist)
+
+ if(base)
+ objs = base->locate_objects(parent, 1) || ({});
+ else
+ {
+ // else get our inv and env
+ objs = environment()->locate_objects(parent, 1)
+ + locate_objects(parent, 1);
+ }
+ objs = filter_objects(objs, "short"); // nur sichtbare...
+ if(sizeof(objs) > 1)
+ return (notify_fail("Es gibt mehr als eine(n) "+capitalize(parent)+".\n"), 0);
+ else
+ {
+ if(sizeof(objs))
+ base = objs[0];
+ else
+ return (notify_fail("Hier ist kein(e) "+capitalize(parent)+".\n"), 0);
+ }
+ objs = 0;
+ }
+ else detail = what;
+
+ // wie auch immer haben wir jetzt ein Bezugsobjekt.
+ int maxtries=3;
+ do {
+ int base_was_env=1;
+ // als ersten werden in Frage kommende Objekte gesucht. Wenn base
+ // existiert (idR nur im ersten Durchlauf), wird dort gesucht, sonst in
+ // Env und Inv.
+ if (base)
+ {
+ if (base == this_object() || base == environment() ||
+ (base->QueryProp(P_TRANSPARENT) && !living(base)))
+ {
+ // ich kann in base reingucken...
+ objs = base->locate_objects(detail, 1) || ({});
+ }
+ else
+ {
+ // Referenzobjekt da, aber nicht reinguckbar. base aber nicht nullen,
+ // denn es ist ja noch gueltig fuer Detailsuchen an base...
+ objs = ({});
+ }
+ }
+ else
+ {
+ objs = environment()->locate_objects(detail, 1)
+ + locate_objects(detail, 1);
+ base = environment();
+ }
+ objs = filter_objects(objs, "short"); // nur sichtbare...
+
+ if (sizeof(objs))
+ {
+ // Objekte gefunden, mal schauen, ob die taugen (d.h. fuer den jew. Sinn
+ // Infos haben. Wenn nicht, muessen wir weitersuchen.
+ // Aber erstmal die Objekte durchlaufen.
+ foreach(object ob: objs)
+ {
+ if (sense == SENSE_READ)
+ {
+ // Extrawurst: P_READ_MSG auch noch abfragen.
+ out = ob->QueryProp(P_READ_MSG);
+ if (!stringp(out))
+ out = ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),SENSE_READ);
+ }
+ else
+ out=ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),sense);
+ if (stringp(out))
+ {
+ SetProp(P_REFERENCE_OBJECT, ob);
+ tell_object(ME, out);
+ return 1;
+ }
+ }
+ }
+
+ // Keine Objekte gefunden, die in Frage kommen. Nach Details suchen.
+ if(out = base->GetDetail(detail, QueryProp(P_REAL_RACE),sense))
+ {
+ SetProp(P_REFERENCE_OBJECT, base);
+ return (write(out), 1);
+ }
+ else
+ {
+ // Auch keine Details gefunden... Wenn wir uns noch das Env angucken
+ // koennen (weil base != env), dann machen wir das, ansonsten ist
+ // jetzt hier leider Ende...
+ if(base == environment())
+ {
+ if (base_was_env)
+ SetProp(P_REFERENCE_OBJECT, 0);
+ return 0;
+ }
+ else
+ {
+ // nochmal im naechsten Schleifendurchlauf ohne base probieren.
+ base = 0;
+ base_was_env = 0;
+ }
+ }
+ } while(--maxtries);
+
+ // nach dieser Schleife sollte man nie ankommen...
+ raise_error(sprintf("_sense_exa(): zuviele Versuche, etwas zu finden."));
+
+ return 0;
+}
+
+varargs int look_into(string str,int mode)
+{
+ object *found_obs;
+
+ if( CannotSee() ) return 1;
+ _notify_fail("Wo willst Du denn reinschauen ?\n");
+ found_obs=find_obs(str,PUT_GET_NONE);
+ if (!found_obs)
+ {
+ if (environment() &&
+ (environment()->GetDetail(str,QueryProp(P_REAL_RACE))||
+ environment()->GetDoorDesc(str)))
+ _notify_fail("Da kannst Du so nicht reinsehen.\n");
+ return 0;
+ }
+ return _examine(str, mode);
+}
+
+/* Gebe die Umgebung des aktiven Spielers zurueck, lasse dabei */
+/* rekursiv geschachtelte Raeume zu. */
+/* Wenn allow_short 0 ist, so wird immer die long-descr benutzt */
+varargs string env_descr(int allow_short,int flags, int force_short )
+{
+ object env;
+ int brief;
+
+ env = environment(ME);
+
+ if(!env)
+ return "Du schwebst im Nichts ... Du siehst nichts, rein gar nichts ...\n";
+
+ if (!force_short && (!allow_short || !QueryProp(P_BRIEF)))
+ return env->int_long(ME,ME,flags);
+
+ if (!flags && ((brief=QueryProp(P_BRIEF))>=2))
+ return "";
+
+ return env->int_short(ME,ME);
+}
+
+int _look(string str)
+{
+ string s;
+ int flag;
+
+ if(CannotSee()) return 1;
+
+ if(!str)
+ {
+ SetProp(P_REFERENCE_OBJECT, 0);
+ write( env_descr() );
+ return 1;
+ }
+ if(str=="-f" || str=="genau")
+ {
+ SetProp(P_REFERENCE_OBJECT, 0);
+ write( env_descr(0,2) );
+ return 1;
+ }
+ if(str=="-k" || str=="kurz")
+ {
+ SetProp(P_REFERENCE_OBJECT, 0);
+ write( env_descr(1,2,1) );
+ return 1;
+ }
+ if(str[0..2]=="-f "){
+ flag=2;
+ str=str[3..];
+ }
+ else if(str[0..5]=="genau "){
+ flag=2;
+ str=str[6..];
+ }
+ else flag = 0;
+ if (sscanf(str,"%s an",s)) str=s;
+ if (sscanf(str,"%s in mir",s)||sscanf(str,"%s in dir",s)) return _examine(str,flag);
+ if (sscanf(str,"in %s",s)) return look_into(s,flag);
+ return _examine(str,flag);
+}
+
+int _equipment(string arg)
+{
+ if (CannotSee()) return 1;
+ call_other("/std/player/invmaster/invmaster", "ShowInv", ME, arg);
+ return 1;
+}
+
+static mixed _query_localcmds()
+{
+ return
+ ({({"ausruestung", "_equipment",0,0}),
+ ({"i","_inventory",0,0}),
+ ({"inv","_inventory",0,0}),
+ ({"inventur","_inventory",0,0}),
+ ({"schau","_look",0,0}),
+ ({"schaue","_look",0,0}),
+ ({"unt","_examine",0,0}),
+ ({"untersuch","_examine",0,0}),
+ ({"betracht","_examine",0,0}),
+ ({"untersuche","_examine",0,0}),
+ ({"betrachte","_examine",0,0}),
+ ({"betr","_examine",0,0}),
+ ({"lausche","_sense_exa",0,0}),
+ ({"lausch","_sense_exa",0,0}),
+ ({"hoer","_sense_exa",0,0}),
+ ({"hoere","_sense_exa",0,0}),
+ ({"lies","_sense_exa",0,0}),
+ ({"lese","_sense_exa",0,0}),
+ ({"les","_sense_exa",0,0}),
+ ({"schnupper","_sense_exa",0,0}),
+ ({"schnuppere","_sense_exa",0,0}),
+ ({"riech","_sense_exa",0,0}),
+ ({"rieche","_sense_exa",0,0}),
+ ({"taste","_sense_exa",0,0}),
+ ({"beruehre","_sense_exa",0,0}),
+ ({"beruehr","_sense_exa",0,0}),
+ ({"kurz","_toggle_brief",0,0}),
+ ({"lang","_toggle_brief",0,0}),
+ ({"ultrakurz","_toggle_brief",0,0})
+ });
+}