blob: 9c9a6e1f48bec329c200c86188b99facca21608e [file] [log] [blame]
// 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("Dieser 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: fett unterstrichen "+
"blinkend invers\n");
if(str == "ansi") {
printf("ANSI Farben und VT100 Attribute:\n");
foreach(int fg: 30 .. 37) {
foreach(int bg: 40 .. 47) {
printf("[%d;%dm@", fg, bg);
printf("[%d;%dm@", fg, bg);
printf("[%d;%dm@", fg, bg);
printf("[%d;%dm@", 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;
}