blob: af068449c77d1ccd6ce69bfc230eb405b50cbbf9 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// player/base.c -- the basic player object
4//
5// $Id: base.c 9467 2016-02-19 19:48:24Z Zesstra $
6#pragma strong_types
7#pragma save_types
8#pragma range_check
9#pragma no_clone
10#pragma pedantic
11
12#include <sys_debug.h>
13#include <regexp.h>
14#include <input_to.h>
15#include <logging.h>
16#include <werliste.h>
17#include <time.h>
18#include <errord.h>
19#include <wizlevels.h>
20#include <money.h>
21
22inherit "/std/hook_provider";
23inherit "/std/player/restrictions";
24inherit "/std/living/attributes";
25inherit "/std/living/put_and_get";
26inherit "/std/living/clothing";
27inherit "/std/thing/properties";
28inherit "/std/player/util";
29inherit "/std/thing/language";
30inherit "/std/player/travel";
31inherit "/std/player/combat";
32inherit "/std/player/description";
33inherit "/std/player/moving";
34inherit "/std/player/life";
35inherit "/std/player/comm";
36inherit "/std/player/viewcmd";
37inherit "/std/player/moneyhandler";
38inherit "/std/player/command";
39inherit "/std/living/skill_attributes";
40inherit "/std/living/light";
41inherit "/std/player/skills";
42inherit "/std/player/quests";
43inherit "/std/player/potion";
44inherit "/std/player/soul";
45inherit "/std/more";
46inherit "/std/user_filter";
47inherit "/secure/telnetneg";
48inherit "/std/player/guide";
49inherit "/std/player/reputation";
50inherit "/std/player/protocols/gmcp";
51inherit "/std/living/helpers";
52
53#define NEED_PROTOTYPES
54#include <player/skills.h>
55#include <player/gmcp.h>
Zesstraa64deb12016-08-31 22:22:07 +020056#include <player/base.h>
MG Mud User88f12472016-06-24 23:31:02 +020057#undef NEED_PROTOTYPES
58#include <player.h>
59#include <properties.h>
60#include <udp.h>
61#include <config.h>
62#include <ansi.h>
63#include <wizlevels.h>
64#include <living.h>
65#include <attributes.h>
66#include <language.h>
67#include <moving.h>
68#include <defines.h>
69#include <terminal.h>
70#include <new_skills.h>
71#include <pager.h>
72#include <combat.h>
73#include "/secure/questmaster.h"
74#include "/secure/lepmaster.h"
75#include <events.h>
76
77#undef NAME /* DEFINED BY UDP.H; BAD NAME CLASH :( */
78#define NAME(who) capitalize(getuid(who))
79
80mapping autoload; /* autoload-mapping */
81int hc_play;
82
83private nosave mapping autoload_rest;
84private nosave string *autoload_error;
85private nosave string realip;
86
87private nosave string passw; /* temporarily for password change */
88private nosave string passwold; /* temporarily for password change */
89
90// HB-Zaehler. Wenn 0 erreicht wird, wird ein Telnet TM Paket als Keep-Alive
91// an den Client gesendet und der Counter wieder auf hochgesetzt.
92// Wenn == 0, ist das Keep-Alive abgeschaltet.
93private int telnet_tm_counter;
94
95nosave string default_home; /* Where to move us if we dont have a home set */
96
97nosave int ndead_lasttime;
98nosave mixed ndead_location;
99nosave string ndead_l_filename;
100nosave int ndead_currently;
101nosave int ndead_next_check;
102nosave object *hb_obs;
103
104private nosave string default_pray_room;
105
106nosave mixed env_ndead_info;
107
108static int _set_invis(int a);
109static string _set_tty(string str);
110static mixed _set_fraternitasdonoarchmagorum(mixed arg);
111
112
113static string al_to_title(int a);
114
115static void ndead_revive();
116
117static int wegmeldung(string player);
118
119int quit();
120void save_me(mixed value_items);
121varargs int remove(mixed arg);
122mixed RaceDefault(string arg);
123
124/** Setzt Defaultwerte vor dem Laden des Savefiles.
125 Nur die Werte bleiben spaeter uebrig, die NICHT aus dem Savefile geladen
126 werden und diese hier ersetzen.
127 Ausserdem kann man hier nicht mit den Werten aus den Savefiles arbeiten.
128 Hierzu muss man updates_after_restore() verwenden.
129*/
130protected void create()
131{
132 if(QueryProp(P_LEVEL))
133 {
134 return; // darf nur EINMAL gemacht werden
135 }
136 call_out("checkConsistency", 0);
137
138 ndead_next_check=NETDEAD_CHECK_TIME;
139 ndead_lasttime=0;
140 ndead_location=0;
141 ndead_l_filename=0;
142 ndead_currently=0;
143 ndead_next_check=0;
144 hc_play=0;
145
146 command::create();
147 properties::create();
148 description::create();
149 light::create();
150 attributes::create();
151 clothing::create();
152 combat::create();
153 life::create();
154 comm::create();
155 viewcmd::create();
156 quests::create();
157 restrictions::create();
158 moving::create();
159 travel::create();
160 skills::create();
161
162 SetProp(P_LEVEL, -1);
163 Set(P_LEVEL, SAVE|SECURED, F_MODE_AS);
164 Set(P_GHOST, SAVE, F_MODE_AS);
165 SetProp(P_SCREENSIZE, -1);
166 Set(P_SCREENSIZE, SAVE,F_MODE_AS);
167 Set(P_MORE_FLAGS, SAVE, F_MODE_AS);
168 SetProp(P_WEIGHT_PERCENT,100);
169 SetProp(P_LONG, 0);
170 SetProp(P_TITLE, "der hoffnungsvolle Anfaenger");
171 SetProp(P_ALIGN, 0);
172 SetProp(P_GENDER, NEUTER);
173 Set(P_GENDER, SAVE, F_MODE_AS);
174 SetProp(P_TTY, "vt100");
175 Set(P_TTY, SAVE, F_MODE_AS);
176 SetProp(P_WEIGHT, 75000);
177 SetProp(P_MAX_HP,50);
178 SetProp(P_MAX_SP,50);
179 SetProp(P_MAX_FOOD,100);
180 SetProp(P_MAX_DRINK,100);
181 SetProp(P_MAX_ALCOHOL,100);
182 Set( P_WIMPY, 20, F_VALUE);
183
184 SetProp(P_HANDS, ({" mit blossen Haenden", 30}));
185 Set(P_HANDS, SAVE, F_MODE_AS);
186 SetProp(P_MAX_HANDS, 2);
187
188 Set(P_MARRIED, SAVE, F_MODE_AS);
189 Set(P_EXTRA_LOOK, SAVE, F_MODE_AS);
190 Set(P_SHOW_EXITS, SAVE, F_MODE_AS);
191 Set(P_SHOW_EXITS, 1);
192 Set(P_WANTS_TO_LEARN, SAVE, F_MODE_AS);
193 Set(P_CAN_FLAGS, SAVE, F_MODE_AS);
194 Set(P_TESTPLAYER, SAVE|PROTECTED, F_MODE_AS);
195 Set(P_ALLOWED_SHADOW, SAVE|SECURED, F_MODE_AS);
196 Set(P_SECOND, SAVE, F_MODE_AS);
197 Set(P_INVIS, SAVE, F_MODE_AS);
198 Set(P_READ_NEWS, SAVE, F_MODE_AS);
199 Set(P_START_HOME, SAVE, F_MODE_AS);
200 Set(P_PRAY_ROOM, SAVE, F_MODE_AS);
201 Set(P_MAILADDR, SAVE, F_MODE_AS);
202 Set(P_HOMEPAGE, SAVE, F_MODE_AS);
203 Set(P_ICQ, SAVE, F_MODE_AS);
204 Set(P_MESSENGER, SAVE, F_MODE_AS);
205 Set(P_LOCATION, SAVE, F_MODE_AS);
206
207 Set(P_NO_ASCII_ART, SAVE, F_MODE_AS);
208
209 Set(P_VISUALBELL, SAVE, F_MODE_AS);
210 Set(P_CARRIED_VALUE, SAVE, F_MODE_AS);
211
212 Set(P_PROMPT, "> ");
213 Set(P_PROMPT, SAVE, F_MODE_AS);
214 Set(P_CALLED_FROM_IP, SAVE, F_MODE_AS);
215 Set(P_INFORMME,SAVE|PROTECTED,F_MODE_AS);
216 Set(P_WAITFOR,SAVE|PROTECTED,F_MODE_AS);
217 Set(P_WAITFOR_REASON,SAVE|PROTECTED,F_MODE_AS);
218 Set(P_DAILY_PLAYTIME,SAVE|PROTECTED,F_MODE_AS);
219 Set(P_NETDEAD_ENV, PROTECTED|NOSETMETHOD, F_MODE_AS);
220
221 autoload = ([]);
222 autoload_rest = ([]);
223 autoload_error = ({});
224 SetProp(P_ARTICLE,0);
225 Set(P_GUILD,SAVE,F_MODE_AS);
226 Set(P_GUILD_TITLE,SAVE,F_MODE_AS);
227 Set(P_GUILD_LEVEL,SAVE,F_MODE_AS);
228 Set(P_GUILD_RATING,SAVE,F_MODE_AS);
229 Set(P_NEWSKILLS,SAVE,F_MODE_AS);
230 Set(P_NEEDED_QP,REQ_QP);
231 Set(P_DEADS,0);
232 Set(P_DEADS, NOSETMETHOD,F_SET_METHOD);
233 Set(P_DEADS,SAVE|PROTECTED|SECURED,F_MODE_AS);
234 Set(P_LAST_LOGIN,-1);
235 Set(P_LAST_LOGIN, NOSETMETHOD,F_SET_METHOD);
236 Set(P_LAST_LOGIN,SAVE|PROTECTED|SECURED,F_MODE_AS);
237 Set(P_LAST_LOGOUT,-1);
238 Set(P_LAST_LOGOUT, NOSETMETHOD,F_SET_METHOD);
239 Set(P_LAST_LOGOUT,SAVE|PROTECTED|SECURED,F_MODE_AS);
240 Set(P_CLOCKMSG,SAVE,F_MODE_AS);
241 Set(P_TIMEZONE,SAVE,F_MODE_AS);
242 Set(P_SHOWEMAIL,SAVE,F_MODE_AS);
243 Set(P_LAST_QUIT,SAVE|PROTECTED|SECURED,F_MODE_AS);
244
245 Set(P_CMSG, 0, F_MODE); // to clean out the old clone messages
246 Set(P_DMSG, 0, F_MODE);
247 Set(P_CLONE_MSG, SAVE, F_MODE);
248 SetProp(P_CLONE_MSG, "zaubert etwas hervor");
249 Set(P_DESTRUCT_MSG, SAVE, F_MODE);
250 SetProp(P_DESTRUCT_MSG, "verschwindet einfach");
251
252 Set(P_FAO, SAVE|SECURED, F_MODE_AS);
253 Set(P_FAO_PORTALS, SAVE, F_MODE_AS);
254
255 SetProp(P_NEWBIE_GUIDE,0);
256 Set(P_NEWBIE_GUIDE,SAVE,F_MODE_AS);
257
258 //TODO: Remove - Property ist not needed any more.
259 Set(P_TELNET_KEEP_ALIVE, PROTECTED|SAVE, F_MODE_AD);
260 SetProp(P_TELNET_KEEP_ALIVE, 0);
261
262 AddId("Interactive");
263
264 realip="";
265}
266
267// ACHTUNG: Falls hier mal sonst noch weitere Resets geerbt werden, muss das
268// hier ordentlich definiert werden!
269void reset() {
270 comm::reset();
271 // momentan kann der Reset jetzt abgeschaltet werden, da er nur die
272 // TM-History loescht und ggf. von Netdead() und Disconnect() reaktiviert
273 // wird.
274 set_next_reset(-1);
275}
276
277protected void NotifyMove(object dest, object oldenv, int method)
278{
279 moving::NotifyMove(dest,oldenv,method);
280 // ggf. Daten ueber neues Env per GMCP senden.
281 if (dest != oldenv)
282 GMCP_Room();
283}
284
Bugfix5a9775f2017-02-17 13:48:56 +0100285public void NotifyInsert(object ob, object oldenv)
286{
287 restrictions::NotifyInsert(ob,oldenv);
288 description::NotifyInsert(ob,oldenv);
289}
290
291public void NotifyLeave(object ob, object dest)
292{
293 restrictions::NotifyLeave(ob,dest);
294 description::NotifyLeave(ob,dest);
295}
296
MG Mud User88f12472016-06-24 23:31:02 +0200297string Forschung()
298{
299 return LEPMASTER->QueryForschung();
300}
301
302/** Setzt Defaultwerte fuer Rassen - wird von den Shells ueberschrieben.
303*/
304mixed RaceDefault(string arg)
305{
306 if (!arg)
307 return 0;
308 switch(arg)
309 {
310 case P_HANDS :
311 return ({" mit blossen Haenden",30,DT_BLUDGEON});
312 case P_BODY :
313 return 0;
314 }
315 return 0;
316}
317
318/** Prueft Spielerobjekt auf Konsistenz.
319 Ueberprueft das Spielerobjekt nach kompletter Initialisierung (im
320 callout) auf korrekte Werte einiger Props.
321*/
322void checkConsistency()
323{ mixed h;
324 int m;
325
326 if (pointerp(h=RaceDefault(P_HANDS)) && sizeof(h)>1)
327 m=(int)h[1];
328 else
329 m=30;
330 if((h=Query(P_HANDS))[1] > m && !IS_LEARNER(this_object())) {
331 log_file("inconsistent", sprintf(
332 "[%s] %O: HANDS: %d\n", dtime(time()), this_player(), h[1]));
333 h[1] = m;
334 Set(P_HANDS,h);
335 }
336
337 if (Query(P_BODY)!=(m=RaceDefault(P_BODY)))
338 Set(P_BODY,m);
339
340 if (!Query(P_SECOND_MARK,F_MODE))
341 Set(P_SECOND_MARK,SAVE|PROTECTED,F_MODE_AS);
342 if (!Query(P_TTY_SHOW,F_MODE)&SAVE)
343 {
344 Set(P_TTY_SHOW,0,F_VALUE);
345 Set(P_TTY_SHOW,SAVE,F_MODE_AS);
346 }
347 if (Query(P_TTY_COLS,F_MODE)&SAVE)
348 {
349 Set(P_TTY_COLS,SAVE,F_MODE_AD);
350 Set(P_TTY_ROWS,SAVE,F_MODE_AD);
351 Set(P_TTY_TYPE,SAVE,F_MODE_AD);
352 }
353}
354
355/** Bittet das Spielerobjekt, sich zu zerstoeren.
356 \param[in] silent Flag, ob ohne Textausgaben zerstoert werden soll
357 \return Erfolg der Selbstzerstoerung
358*/
359varargs int remove(int silent)
360{
361 return moving::remove(silent);
362}
363
364/** Schaltet in allen Objekten im Inv HBs aus.
365 Schaltet im Inv in allen Objekten (rekursiv, deep_inventory)
366 die Heartbeats aus. Falls obs uebergeben wird, werden diese Objekte
367 statt des Inventars benutzt.
368 Speichert die Objekte, die einen HB hatten, in der glob. Var. hb_obs.
369 @param[in] obs mixed - Objekte, in denen der HB abgeschaltet werden soll.
370 Container werden rekursiv behandelt.
371 @attention Achtung! Niemals zweimal hintereinander rufen, ohne
372 zwischendurch restart_heart_beats() gerufen zu haben!
373 @sa restart_heart_beats
374*/
375varargs static void stop_heart_beats(mixed obs)
376{
377 int i;
378
379 if (!obs)
380 {
381 hb_obs=({});
382 obs=deep_inventory(ME);
383 }
384 foreach(mixed ob: obs) {
385 if (pointerp(ob))
386 stop_heart_beats(ob);
387 else if (set_object_heart_beat(ob,0))
388 hb_obs+=({ob});
389 }
390}
391
392/** Schaltet HBs in Objekten im Inv wieder ein.
393 Schaltet in allen Objekten in hb_obs den Heartbeat ein.
394 In hb_obs (glob. Var.) stehen alle Objekte, die beim letzten Aufruf von
395 stop_heart_beats() einen HB hatten.
396 @sa stop_heart_beats
397*/
398static void restart_heart_beats()
399{
400 int i;
401
402 if (pointerp(hb_obs))
403 {
404 foreach(object ob: hb_obs)
405 set_object_heart_beat(ob,1);
406 hb_obs=0;
407 }
408}
409
410/** Prueft auf abgelaufene Spielzeit.
411 Prueft in Spielerobjekten, ob die taegliche Maximalspielzeit
412 abgelaufen ist.
413 Wird im heart_beat() gerufen.
414 @return int - Flag, ob Spielzeit abgelaufen und Logout erfolgen soll
415*/
416static int CheckDailyPlaytime() {
417 int *spieldauer,d;
418
419 if (!pointerp(spieldauer=Query(P_DAILY_PLAYTIME)))
420 return 0;
421 // 0:Minuten pro Tag, 1:Nr. des letzen Tages, 2:Nr. des angefangenen Tages,
422 // 3:letzte Zeitpruefung, 4:verbleibende Zeit
423 d=time()/86400;
424 if (spieldauer[1]<=d) { // Ende der zeitbeschraenkten Tage?
425 Set(P_DAILY_PLAYTIME,0);
426 return 0;
427 } else if (spieldauer[2]!=d) { // Neuer Tag?
428 spieldauer[4]=spieldauer[0];
429 spieldauer[2]=d;
430 } else {
431 spieldauer[4]-=(time()-spieldauer[3]);
432 }
433 spieldauer[3]=time(); // Letzte Zeitpruefung
434 Set(P_DAILY_PLAYTIME,spieldauer);
435 if (spieldauer[4]<0) { // Keine Zeit mehr uebrig fuer heute
436 if (!interactive(ME))
437 return 1;
438 write("Du hast lange genug gemuddet fuer heute.\n");
439 say(Name(WER)+" hat fuer heute genug gemuddet.\n");
440 remove_interactive(ME);
441 return 1;
442 }
443 return 0;
444}
445
446/** Gibt Erwartemeldung mit Grund aus.
447 @param[in] who string - Wer ist hereingekommen?
448 @param[in] invis int - Ist der Spieler Invis?
449*/
450static void Show_WaitFor_Reason(string who, int invis)
451{
452 mixed list;
453 string reason,name;
454
455 if (invis) name="("+who+")";
456 else name=who;
457 if ((mappingp(list=QueryProp(P_WAITFOR_REASON))) && (reason=list[who]))
458 tell_object(ME,sprintf("\nDu erwartest %s wegen:\n%s\n",name,reason));
459 else
460 tell_object(ME,sprintf("Du erwartest %s aus keinem bestimmten Grund.\n",
461 name));
462}
463
464/** Gibt Liste der Erwarteten Spieler an this_player() aus.
465*/
466static void ListAwaited() //Anwesende Erwartete auflisten
467{
468 string *list;
469 mixed mlist;
470 object ob;
471 int mag;
472
473 mag=IS_LEARNER(ME);
474
475 list=({});
476 foreach(string erwartet : QueryProp(P_WAITFOR)) {
477 if (objectp(ob=find_player(lower_case(erwartet)))) {
478 if (ob->QueryProp(P_INVIS)) {
479 if (mag) list+=({ sprintf("(%s)",erwartet) });
480 }
481 else list+=({erwartet});
482 }
483 }
484 if (sizeof(list))
485 printf("Anwesende Erwartete: %s.\n",
486 CountUp(sort_array(list,#'>)));
487
488 if ((mappingp(mlist=QueryProp(P_WAITFOR_REASON))) && (sizeof(mlist)))
489 {
490 foreach(string erwartet : mlist) {
491 if (!(ob=find_player(lower_case(erwartet))) ||
492 (!mag && ob->QueryProp(P_INVIS)));
493 else Show_WaitFor_Reason(erwartet,ob->QueryProp(P_INVIS));
494 }
495 }
496}
497/** Teilt den Gilden und anderen Spielern mit, wer reingekommen ist.
498 Ausserdem wird ggf. PlayerQuit() im Environment gerufen.
499 \param[in] rein int - wahr, wenn der Spieler einloggt.
500*/
501protected void call_notify_player_change(int rein)
502{
503 string wer = getuid(ME);
504 // erst die Gilde informieren
505 string gilde = QueryProp(P_GUILD);
506 if (stringp(gilde) && (find_object("/gilden/"+gilde)
507 || file_size("/gilden/"+gilde+".c")>0))
508 catch(("/gilden/"+gilde)->notify_player_change(ME, rein); publish);
509
510 // dann die anderen Spieler
511 int mag = IS_LEARNER(ME);
512 int invis = QueryProp(P_INVIS);
513 object *u = users() - ({ME}); // sich selber nicht melden
514 if (mag && invis) { // Invismagier nur Magiern melden
515 u = filter(u, function int (object o)
516 { return query_wiz_level(o) >= LEARNER_LVL; }
517 );
518 }
519 u->notify_player_change(capitalize(wer),rein,invis);
520
521 // und beim Ausloggen noch das Env informieren.
522 if (!rein) {
523 if(environment()) catch(environment()->PlayerQuit(ME);publish);
524 }
525}
526
527/** Ruft im uebergebenen Objekt ein init() auf, sofern notwendig.
528 Ruft in ob ein init() auf, falls das Objekt nach dem
529 letzten Ausloggen geschaffen wurde.
530 \param[in] ob object - Objekt, in dem init() gerufen wird.
531 \param[in] logout int - Letzter Logout
532 \return 1, falls init() gerufen wurde. 0 sonst.
533*/
534static int call_init( object ob, int logout )
535{
536 if ( objectp(ob) && object_time(ob) > logout )
537 return catch(ob->init(); publish), 1;
538 return(0);
539}
540
541/** Holt seit dem letzten Ausloggen ausgefallene Inits nach.
542 Ruft in den uebergebenen Objekten call_init(), was einen init()
543 ausloest, falls das Objekt seit dem letzten Ausloggen erstellt wurde.
544 \param[in] logout Zeitpunkt des letzten Logouts
545 \param[in] obs Array von Objekten
546 \sa call_init()
547*/
548static void inits_nachholen( int logout, object *obs )
549{
550 filter( obs, "call_init", ME, logout );
551}
552
553/** Belebt einen Netztoten wieder.
554 Wird im Login gerufen, wenn der Spieler netztot war. Aequivalent zu
555 start_player()
556 @param[in] silent Wenn Flag gesetzt, werden keine Meldung an den Raum
557 ausgegeben.
558 @param[in] ip Textuelle Repraesentation der IP-Adresse, von der der Spieler
559 kommt.
560 @see start_player()
561*/
562varargs void Reconnect( int silent )
563{
564 int num;
565 string called_from_ip;
566 object *inv;
567
568 if ( query_once_interactive(ME) )
569 {
570 // perform the telnet negotiations. (all that are available)
571 "*"::startup_telnet_negs();
572 Set( P_LAST_LOGIN, time() );
573 }
574
575 enable_commands();
576 set_living_name( getuid() );
577 _remove_netdead();
578 set_heart_beat(1);
579 // Hunttimes aktualisieren und ggf. Feinde vergessen.
580 update_hunt_times((time()-QueryProp(P_LAST_LOGOUT)) /__HEART_BEAT_INTERVAL__);
581 // Heartbeats in Objekten im Inv reaktiveren.
582 restart_heart_beats();
583 // life.c will ggf. was aufraeumen
584 life::reconnect();
585
Zesstrae88826c2016-09-24 20:37:05 +0200586 log_file( "syslog/shell/REENTER", sprintf( "%-11s %s, %-15s (%s).\n",
MG Mud User88f12472016-06-24 23:31:02 +0200587 capitalize(getuid(ME)), ctime(time())[4..15],
588 query_ip_number(ME)||"Unknown",
589 query_ip_name(ME)||"Unknown" ),
590 200000 );
591
592 if ( ndead_currently )
593 ndead_revive();
594
595 if ( !silent && interactive(ME) )
596 call_notify_player_change(1);
597
598 command::reconnect();
599 if ( query_once_interactive(ME) )
600 modify_prompt();
601
602 // Login-event ausloesen
603 EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
604 E_OBJECT: ME,
605 E_PLNAME: getuid(ME),
606 E_ENVIRONMENT: environment() ]) );
607
608 catch( num = "secure/mailer"->FingerMail(geteuid());publish );
609
610 if ( num )
611 write( "Du hast " + num + " neue" + (num == 1 ? "n Brief" : " Briefe")
612 + " im Postamt liegen.\n" );
613
614 if ( QueryProp(P_AWAY) )
615 write( break_string( "Du bist als abwesend gekennzeichnet: " +
616 QueryProp(P_AWAY) + ".", 78 ) );
617
618 catch( RegisterChannels(); publish );
619
620 if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
621 query_ip_number(ME) != called_from_ip ) {
622 string tmp;
623
624 if ( stringp(tmp = query_ip_name(called_from_ip)) &&
625 tmp != called_from_ip )
626 tmp = " [" + tmp + "]";
627 else
628 tmp = "";
629
630 write( "Das letzte Mal kamst Du von " + called_from_ip + tmp + ".\n" );
631 }
632
633 Set( P_CALLED_FROM_IP, query_ip_number(ME) );
634
635 // falls Gegenstaende mit 'upd -ar' upgedated wurden, muessen die
636 // "verloren gegangenen" init()'s nachgeholt werden
637 if ( query_once_interactive(ME) && !IS_LEARNER(ME) )
638 call_out( "inits_nachholen", 0, Query(P_LAST_LOGOUT),
639 all_inventory(ME) );
640
641 // noch nicht geclonte Autoloader "nach"clonen
642 while ( remove_call_out("load_auto_objects") != -1 )
643 /* do nothing */;
644
645 if ( sizeof(autoload_rest) )
646 call_out( "load_auto_objects", 0, autoload_rest );
647
648 if (ndead_location) {
649 catch( ndead_location->BecomesNetAlive(ME);publish );
650 inv = all_inventory(ndead_location);
651 ndead_location = 0;
652 }
653 else
654 inv = ({});
655
656 inv += deep_inventory(ME);
657
658 //ZZ foreach statt call_other(), damit nen bug in BNA nicht die anderen
659 //BNA verhindert.
660 foreach(object ob: inv) {
661 //es ist nicht auszuschliessen, dass Items durch BecomesNetAlive()
662 //eines anderen zerstoert werden.
663 if (objectp(ob))
664 catch( call_other(ob, "BecomesNetAlive", ME);publish );
665 }
666
667 // Erst an dieser Stelle, weil der Spieler u.U. durch ein BecomesNetAlive()
668 // noch bewegt wurde.
669 if ( !silent && environment() && object_name(environment()) != NETDEAD_ROOM )
670 {
671 if(query_hc_play()<=1)
672 tell_room(environment(),QueryProp(P_NAME) + " weilt wieder unter den Lebenden.\n",({ME}) );
673 else
674 tell_room(environment(),QueryProp(P_NAME) + " weilt wieder unter den Verstorbenen.\n",({ME}) );
675 }
676
677 NewbieIntroMsg();
678
679 if ( query_once_interactive(ME) )
680 ListAwaited();
681}
682
683/** Loggt einen Spieler aus und macht ihn netztot.
684 Bewegt einen Spieler in den Netztotenraum, deaktiviert Heartbeats im
685 Inventar, ruft BecomesNetDead(), loest Erwartemeldungen aus, triggert
686 Ausloggevent.
687*/
688void NetDead()
689{
690 object *inv;
691 int num;
692
693 catch(RemoveChannels();publish);
694
695 if(query_hc_play()>1)
696 say("Ploetzlich weicht alle spirituelle Energie aus "+QueryProp(P_NAME)+".\n");
697 else
698 say("Ploetzlich weicht alles Leben aus "+QueryProp(P_NAME)+".\n");
699
700 _set_netdead();
701 remove_call_out("quit");
702 remove_living_name();
703 // Wird zwar im save_me() gemacht, aber die Zeitunterschiede beim
704 // fingern direkt nach dem Ausloggen fuehren immer wieder zu Verwirrungen
705 if(query_once_interactive(ME) && !QueryProp(P_INVIS))
706 Set(P_LAST_LOGOUT,time());
707 if (ME)
708 ndead_location = environment();
709
710 if (query_once_interactive(ME))
711 call_notify_player_change(0);
712
713 // Logout-event ausloesen
714 EVENTD->TriggerEvent(EVT_LIB_LOGOUT, ([
715 E_OBJECT: ME,
716 E_PLNAME: getuid(ME),
717 E_ENVIRONMENT: environment() ]) );
718
719 set_next_reset(900);
720 /* Bei Nicht-Magier-Shells wird comm::reset() aufgerufen, das prueft, ob
721 der Spieler immer noch netztot ist, und falls ja, die tmhist loescht.
722 Die Methode wird von /std/shells/magier.c ueberschrieben, netztote
723 Magier (die eigentlich schon anderweitig beseitigt worden sein sollten)
724 werden remove()d und destruct()ed. --Amynthor 05.05.2008 */
725
726 if (environment()) {
727 catch(environment()->BecomesNetDead(ME);publish);
728 inv = deep_inventory(ME)+all_inventory(environment());
729 }
730 else inv=deep_inventory(ME);
731 foreach(object ob: inv) {
732 if (objectp(ob)) //man weiss nie was BND() macht...
733 catch( call_other(ob, "BecomesNetDead", ME);publish );
734 }
735}
736
737
738/** Sendet ggf. Telnet Timing Marks als Keep-Alive Pakete an den Client.
739 * Wird in heart_beat() gerufen.
740 * @return 1, falls der Spieler Keep-Alive Paket wuenscht, sonst 0.
741 * @see heart_beat()
742*/
743protected int CheckTelnetKeepAlive() {
744 if (telnet_tm_counter > 0) {
745 // Spieler hat offenbar ein Keep-Alive konfiguriert ...
746 if (!(--telnet_tm_counter)) {
747 // und das Intervall ist gerade abgelaufen.
748 // Prop offenbar gesetzt, FEature ist wirklich gewuenscht,
749 // Telnet Timing Mark senden
750 send_telnet_timing_mark();
751 // alle 120 HBs (240s, 4min).
752 // sollte eigentlich 240 / __HEART_BEAT_INTERVAL__ sein. Aber spart
753 // eine Operation im HB. ;-)
754 telnet_tm_counter = 120;
755 }
756 return 1; // Keep-Alive ist eingeschaltet
757 }
758 return 0;
759}
760
761static void ndead_move_me();
762
763/** Heartbeat des Spielerobjektes.
764 Prueft taegliche Spielzeit, speichert regelmaessig den Spieler,
765 bewegt Netztote in den Netztodenraum, ruft die HBs aus living/combat und
766 player/life.
767*/
768protected void heart_beat() {
769 if (!ME)
770 return;
771 if (ndead_currently)
772 {
773 if (interactive(ME))
774 {
775 ndead_revive();
776 ndead_location=0;
777 }
778 else return;
779 }
780 else
781 if (!(ndead_next_check--))
782 {
783 ndead_next_check=NETDEAD_CHECK_TIME;
784 if (!interactive(ME))
785 if (ndead_lasttime)
786 {
787 save_me(1);
788 if (IS_LEARNER(ME))
789 {
790 quit();
791 if (ME)
792 remove();
793 if (ME)
794 destruct(ME);
795 return;
796 }
797 ndead_move_me();
798 // Zumindest bei Gaesten ist das Objekt jetzt zerstoert
799 if (!objectp(this_object()))
800 return;
801 }
802 else
803 ndead_lasttime=1;
804 }
805 if (ME && ndead_lasttime && interactive(ME))
806 ndead_lasttime=0;
807 if (CheckDailyPlaytime())
808 return;
809
810 CheckTelnetKeepAlive();
811
812 life::heart_beat();
813 combat::heart_beat();
814 skills::heart_beat();
815}
816
817/** ID-Funktion fuer Spielerobjekte.
818 * id() fuer Spieler. Besondere Behandlung fuer Froesche und Geister,
819 * sowie Invis-Status.
820 * Gibt immer 0 zurueck, wenn P_INVIS && lvl < P_LEVEL
821 * @param[in] str angefragter ID-String
822 * @param[in] lvl Level des Anfragenden
823 * @return 1, falls str auf den Spieler zutrifft, 0 sonst.
824 */
825varargs int id(string str, int lvl)
826{
827 if (::id(str))
828 return 1;
829 if (Query(P_INVIS) && lvl < QueryProp(P_LEVEL))
830 return 0;
831 if (QueryProp(P_GHOST)&& str == "geist von "+ lower_case(QueryProp(P_NAME)))
832 return 1;
833 if (QueryProp(P_FROG) && str == "frosch") return 1;
834 return(0);
835}
836
837/** Setzt Spielerhomepage (Spielerkommando).
838 * @param[in] str Spielereingabe
839 * @return 1 bei Erfolg, 0 sonst.
840 */
841static int set_homepage(string str)
842{
843 mixed tmp;
844 if (!(str=_unparsed_args())) {
845 if (!QueryProp(P_HOMEPAGE))
846 write("Du hast keine URL-Adresse gesetzt!\n");
847 else
848 write("Deine offizielle URL-Adresse lautet: " + QueryProp(P_HOMEPAGE)
849 +"\n");
850 return 1;
851 }
852 write("Deine offizielle URL-Adresse wurde geaendert.\n");
853 if (str=="keine")
854 SetProp(P_HOMEPAGE, 0);
855 else {
856 tmp = filter(regexplode(str, "[<][^>]*[>]"),
857 lambda(({'e}), ({#'!=, ({#'[, 'e, 0}), '<'})));
858 write("Sie lautet jetzt: "+(str = implode(tmp, ""))+"\n");
859 SetProp(P_HOMEPAGE, str);
860 }
861 return 1;
862}
863
864/** Setzt Spieler-Wohnort (Spielerkommando).
865 * \param[in] str Spielereingabe
866 * \return 1 bei Erfolg, 0 sonst.
867 */
868static int set_location( string str )
869{
870 mixed ort;
871
872 if ( str == "0" || str == "loeschen" ){
873 Set( P_LOCATION, 0 );
874 write( "Du loescht Deine Ortsangabe.\n" );
875 return 1;
876 }
877
878 if ( stringp(str = _unparsed_args()) && str != "" ){
879 Set( P_LOCATION, capitalize(str) );
880 printf( "Du aenderst Deine Ortsangabe auf \"%s\".\n",
881 Query(P_LOCATION) );
882 }
883 else if ( stringp(ort = Query(P_LOCATION)) )
884 printf( "Deine Ortsangabe lautet \"%s\".\n", Query(P_LOCATION) );
885 else{
886 Set( P_LOCATION, 0, F_VALUE );
887 write( "Du hast keine Ortsangabe gesetzt.\n" );
888 }
889
890 return 1;
891}
892
893/** Setzt ICQ-UIN des Spielers (Spielerkommando).
894 * \param[in] str Spielereingabe
895 * \return 1 bei Erfolg, 0 sonst.
896 */
897static int set_icq(string str) {
898 int num;
899
900 if (!str || str=="") {
901 if (!num=QueryProp(P_ICQ))
902 write("Du hast keine ICQ-Nummer gesetzt!\n");
903 else
904 printf("Deine ICQ-Nummer lautet: %d\n",num);
905 return 1;
906 }
907 if (sscanf(str,"%d",num)!=1 || !num) {
908 write("Deine ICQ-Nummer wurde geloescht.\n");
909 SetProp(P_ICQ, 0);
910 } else {
911 write("Deine ICQ-Nummer wurde geaendert.\n");
912 printf("Sie lautet jetzt: %d\n",num);
913 SetProp(P_ICQ, num);
914 }
915 return 1;
916}
917
918/** Setzt Instant Messanger vom Spieler (Spielerkommando).
919 * \param[in] str Spielereingabe
920 * \return 1 bei Erfolg, 0 sonst.
921 */
922static int set_messenger(string str) {
923 int num;
924 string s;
925
926 if (!str || str=="") {
927 if (!s=QueryProp(P_MESSENGER))
928 if (!num=QueryProp(P_ICQ))
929 write("Du hast keine Messenger-ID gesetzt.\n");
930 else
931 printf("Du hast keine Messenger-ID gesetzt, aber eine ICQ-Nummer: %d\n", num);
932 else
933 printf("Deine Messenger-ID lautet: %s\n", s);
934 return 1;
935 }
936 if (str=="loeschen" || str=="keine") {
937 write("Deine Messenger-ID wurde geloescht.\n");
938 SetProp(P_MESSENGER, 0);
939 } else {
940 s = _unparsed_args();
941 printf("Deine Messenger-ID lautet nun: %s\n", s);
942 SetProp(P_MESSENGER, s);
943 }
944 return 1;
945}
946
947
948// Prueft, ob der String vermutlich eine eMail-Adresse ist.
949// dies ist nicht narrensicher, wird aber die meisten eMail-Adressen zulassen
950// und viel Schrott ablehnen.
951private string check_email(string str) {
952 if (!stringp(str)) return 0;
953 return regmatch(lower_case(str),
954 "[a-z0-9._%+-]+@(?:[a-z0-9-]+\.)+[a-z]{2,4}",RE_PCRE);
955}
956
957/** Setzt Email-Adresse des Spielers (Spielerkommando).
958 * \param[in] str Spielereingabe
959 * \return 1 bei Erfolg, 0 sonst.
960 */
961static int set_email(string str)
962{
963 if (!(str=_unparsed_args())) {
964 write("Deine offizielle Email-Adresse lautet: " + QueryProp(P_MAILADDR)
965 +"\n");
966 return 1;
967 }
968 str = check_email(str);
969 if (!str) {
970 notify_fail("Deine Eingabe scheint keine gueltige EMail-Adresse "
971 "zu sein.\n");
972 return 0;
973 }
974 write("Deine EMail-Adresse wurde geaendert zu:\n"
975 +str+"\n");
976 SetProp(P_MAILADDR, str);
977 return 1;
978}
979
980/** Spielerkommando 'selbstloeschung'.
981 * Gibt Meldung aus und fragt nach einer Bestaetigung.
982 * \return 1 bei Erfolg, 0 sonst.
983 */
984static int self_delete()
985{
Zesstra0dc75be2017-01-29 12:34:13 +0100986 string msg = sprintf("%s\n"
987 "Wenn Du Dich selbstloeschen willst, ist Dein Charakter UNWIDERRUFLICH"
988 "verloren. Es gibt KEINE Moeglichkeit, ihn wiederzuerschaffen. Solltest"
989 "Du nur zeitweilig vom "MUDNAME" wegbleiben wollen, so benutze bitte"
MG Mud User88f12472016-06-24 23:31:02 +0200990 "den Befehl 'spielpause'.\n"+
Zesstra0dc75be2017-01-29 12:34:13 +0100991 "Fallst Du %s immer noch selbstloeschen willst, gib Dein Password "
992 "ein.\n\n",
993 (QueryProp(P_NO_ASCII_ART) ? "Bist Du Dir wirklich sicher?\n"
994 : " B I S T D U D I R W I R K L I C H S I C H E R ???????\n"),
995 query_real_name());
996 write(break_string(msg,78,0,BS_LEAVE_MY_LFS));
MG Mud User88f12472016-06-24 23:31:02 +0200997 input_to("self_delete2",INPUT_PROMPT|INPUT_NOECHO, "Bitte das Password angeben: ");
998 return 1;
999}
1000
1001/** Spielerkommando 'selbstloeschung'.
1002 * Empfaengt Bestaetigung des Spielers und ruft Loeschfunktion in
1003 * /secure/master auf.
1004 * \param[in] str Spielereingabe
1005 * \return 1 bei Erfolg, 0 sonst.
1006 */
1007int self_delete2(string str)
1008{
1009 int ret;
1010 ret=(int)"secure/master"->delete_player(str, getuid(PL));
1011 if (!ret)
1012 {
1013 write("Das hat nicht hingehauen (Gott sei Dank ....)\n");
1014 return 1;
1015 }
1016 if (QueryProp(P_GUILD)&&file_size(GUILD_DIR+QueryProp(P_GUILD)+".c")>-1)
1017 catch(call_other(GUILD_DIR+QueryProp(P_GUILD), "austreten");publish);
1018
1019 if (QueryProp(P_DEADS) < 5) {
1020 write("Adios! Man sieht sich.\n");
1021 say(name(WER,1)+" hat sich gerade selbst zerstoert.\n");
1022 }
1023 else {
1024 write(
1025 "\nTod kommt auf seinem weissen Pferd angeritten.\n"
1026 +"Er steigt ab, baut sich drohend vor Dir auf und mustert Dich schadenfroh.\n"
1027 +"\nTod sagt: ENDLICH! NUN KANN DIR AUCH LARS NICHT MEHR HELFEN!\n"
1028 +"\nTod holt weit mit seiner Sense aus. Mit grossem Schwung laesst er sie auf\n"
1029 +"Dich zusausen und dann...\n");
1030 say(name(WER,1)+" schied gerade endgueltig von uns.\n");
1031 }
1032
1033 // Event ausloesen. ;-)
1034 EVENTD->TriggerEvent(EVT_LIB_PLAYER_DELETION, ([
1035 E_PLNAME: getuid(ME),
1036 E_ENVIRONMENT: environment(),
1037 E_GUILDNAME: QueryProp(P_GUILD) ]) );
1038
1039 remove(1);
1040 return 1;
1041}
1042
1043/** Setzt neue taegliche Spieldauer (Spielerkommando).
1044 * \param[in] str Spielereingabe
1045 * \return 1 bei Erfolg, 0 sonst.
1046 */
1047static int spieldauer(string str) {
1048 int min,day;
1049 string foo;
1050
1051 notify_fail(" spieldauer <x> minuten fuer %d tage\noder\n"+
1052 " spieldauer <x> stunden fuer %d tage\n");
1053 if (!str)
1054 return 0;
1055 if (sscanf(str,"%d stunde%s fuer %d tag%s",min,foo,day,foo)==4)
1056 min*=60;
1057 else if (sscanf(str,"%d minute%s fuer %d tag%s",min,foo,day,foo)!=4)
1058 return 0;
1059 if (min<5)
1060 min=5;
1061 if (min>=1440)
1062 return notify_fail("Witzbold.\n"),0;
1063
1064 Set(P_DAILY_PLAYTIME,
1065 ({min*60,time()/86400+day,time()/86400,time(),min*60}));
1066 // 0:Minuten pro Tag, 1:Nr. des letzen Tages, 2:Nr. des angefangenen Tages,
1067 // 3:letzte Zeitpruefung, 4:verbleibende Zeit
1068 printf("Du darfst die naechsten %d Tag(e) nur noch\n"+
1069 "%d Minuten am Tag mudden.\n",day,min);
1070 return 1;
1071}
1072
1073/** Interpretiert Angabe des Spielers fuer Spielpause.
1074 * \param[in] a Zeitangabe fuer Spielpause.
1075 * \return Zeitpunkt, Ende der Spielpause.
1076 */
1077private int InterpretTime(string a){
1078 // akzeptiert folgende Formate:
1079 // dd.mm.jj (Rueckgabe: 0:00 des entsprechenden Tages)
1080
1081 int *ts = allocate(9);
1082 int i,j,k,nrargs;
1083
1084 if ((nrargs=sscanf(a,"%d.%d.%d",i,j,k))==3 ||
1085 (nrargs=sscanf(a,"%d.%d.",i,j))==2) {
1086 // wenn kein jahr angegeben ist, das aktuelle nehmen.
1087 if (nrargs == 2)
1088 ts[TM_YEAR] = localtime()[TM_YEAR];
1089 else {
1090 // Zwei-Ziffern-Angabe des Jahres...
1091 if (k<100)
1092 k += 2000;
1093 ts[TM_YEAR] = k;
1094 }
1095 ts[TM_MDAY] = i;
1096 ts[TM_MON] = j - 1;
1097
1098 int zeit = mktime(ts);
1099
1100 // negative und vergangene Zeiten pruefen.
1101 if (zeit <= time()) {
1102 write("Dieser Zeitpunkt liegt in der Vergangenheit.\n");
1103 return 0;
1104 }
1105 return zeit;
1106 }
1107 return 0;
1108}
1109
1110/** Setzt neue Spielpause (Spielerkommando).
1111 * Fragt vorher nach Bestaetigung durch den Spieler.
1112 * \param[in] str Spielereingabe
1113 * \return 1 bei Erfolg, 0 sonst.
1114 * \sa spielpause2()
1115 */
1116static int spielpause(string str)
1117{
1118 int days,endezeit;
1119 string foo;
1120
1121 notify_fail("spielpause <x> tage oder\n"+
1122 "spielpause bis tt.mm[.jj]\n");
1123 if (!str) return 0;
1124 if(sscanf(_unparsed_args(),"bis %s",foo)==1) {
1125 endezeit = InterpretTime(foo);
1126 if (endezeit == 0)
1127 return 0;
1128 days = ((endezeit - time()) / 86400) + 1;
1129 }
1130 else if(sscanf(str, "%d tag%s", days, foo) == 2) {
1131 if (days < 0)
1132 days = -1;
1133 else
1134 endezeit = (time()/86400) * 86400 + days * 86400;
1135 }
1136 else return 0;
1137
1138 if (days > 0)
1139 write(strftime("Du wirst Dich erst wieder am %d.%m.%Y einloggen koennen!\n",
1140 endezeit));
1141 else if (days < 0)
1142 write( "Du wirst Dich auf unbestimmte Zeit nicht mehr einloggen koennen.\n"
1143 +"Wenn Du wieder spielen willst, musst Du Dich an einen Gott oder\n"
1144 +"Erzmagier wenden (mit einem Gast oder Mail von aussen).\n" );
1145 else {
1146 write( "Die Spielpause ist aufgehoben.\n" );
1147 master()->TBanishName(getuid(this_object()), 0);
1148 return 1;
1149 }
1150 write( "Wenn Du das wirklich willst, gib jetzt 'ja' ein.\n" );
1151 input_to( "spielpause2", INPUT_PROMPT, "]", days);
1152 return 1;
1153}
1154
1155/** Setzt neue taegliche Spieldauer, wird ueber spielpause() gerufen.
1156 * \param[in] str Spielereingabe, Bestaetigung
1157 * \param[in] days Dauer der Spielpause (in Tagen).
1158 * \sa spielpause()
1159 */
1160static void spielpause2(string str, int days)
1161{
1162 if (str && (str == "ja" || str == "Ja" || str == "JA")) {
1163 master()->TBanishName(getuid(this_object()), days);
1164 write(
1165 "Ok, die Spielpause wird mit dem naechsten Ausloggen wirksam.\n"
1166 +"Solltest Du es Dir bis dahin noch einmal ueberlegt haben, so kannst\n"
1167 +"Du den Vorgang mit 'spielpause 0 tage' wieder rueckgaengig machen.\n" );
1168 return;
1169 }
1170 write("Vorgang wurde abgebrochen.\n" );
1171}
1172
1173/** Setzt neues Passwort (Spielerkommando).
1174 * Fragt nach altem Passwort und ruft change_password2().
1175 * \return 1 bei Erfolg, 0 sonst.
1176 * \sa change_password2(), change_password3(), change_password4()
1177 */
1178static int change_password() {
1179 string verb;
1180 verb=query_verb();
1181 if (verb!="passwd"&&verb!="password"&&verb!="passwort")
1182 return 0;
1183 input_to("change_password2",INPUT_NOECHO|INPUT_PROMPT,
1184 "Bitte das ALTE Passwort angeben: ");
1185 return 1;
1186}
1187
1188/** Setzt neues Passwort (Spielerkommando).
1189 * Prueft altes Passwort, fragt nach neuem Passwort und ruft
1190 * change_password3().
1191 * \param[in] str Spielereingabe des alten Passwortes
1192 * \return 1 bei Erfolg, 0 sonst.
1193 * \sa change_password(), change_password3(), change_password4()
1194 */
1195static int change_password2(string str) {
1196 write("\n");
1197 if (!str)
1198 str="";
1199 if (MASTER->update_password(str,str) == 0) {
1200 write("Falsches Passwort!\n");
1201 return 1;
1202 }
1203 passwold = str;
1204 input_to("change_password3",INPUT_NOECHO|INPUT_PROMPT,
1205 "Bitte das NEUE Passwort eingeben: ");
1206 return 1;
1207}
1208
1209/** Setzt neues Passwort (Spielerkommando).
1210 * Prueft neues Passwort, fragt nach Bestaetigung des neues
1211 * Passwortes und ruft change_password4().
1212 * \param[in] str Spielereingabe des neuen Passwortes
1213 * \return 1 bei Erfolg, 0 sonst.
1214 * \sa change_password(), change_password2(), change_password4()
1215 */
1216static int change_password3( string str )
1217{
1218 write( "\n" );
1219
1220 if ( !str || str == "" ){
1221 write( "Abgebrochen !\n" );
1222 passwold = passw = 0;
1223 return 1;
1224 }
1225
1226 if ( passwold == str ){
1227 write( "Das war Dein altes Passwort.\n" );
1228 input_to( "change_password3", INPUT_NOECHO|INPUT_PROMPT,
1229 "Bitte das NEUE Passwort eingeben (zum Abbruch Return druecken): ");
1230 return 1;
1231 }
1232
1233 if ( !MASTER->good_password( str, getuid(ME) ) ){
1234 input_to( "change_password3", INPUT_NOECHO|INPUT_PROMPT,
1235 "Bitte das NEUE Passwort eingeben: ");
1236 return 1;
1237 }
1238
1239 passw = str;
1240 input_to( "change_password4", INPUT_NOECHO|INPUT_PROMPT,
1241 "Bitte nochmal: ");
1242 return 1;
1243}
1244
1245/** Setzt neues Passwort (Spielerkommando).
1246 * Prueft neues Passwort und setzt neues Passwort.
1247 * \param[in] str Spielereingabe des neuen Passwortes
1248 * \return 1 bei Erfolg, 0 sonst.
1249 * \sa change_password(), change_password2(), change_password3()
1250 */
1251static int change_password4( string str )
1252{
1253 write( "\n" );
1254
1255 if ( !str || str != passw ){
1256 write( "Das war verschieden! Passwort NICHT geaendert.\n" );
1257 passwold = passw = 0;
1258 return 1;
1259 }
1260
1261 if ( MASTER->update_password( passwold, passw ) )
1262 write( "Passwort geaendert.\n" );
1263 else
1264 write( "Hat nicht geklappt!\n" );
1265
1266 passwold = passw = 0;
1267 return 1;
1268}
1269
1270
1271/*
1272 *-----------------------------------------------------------------
1273 * Rueckmeldungen von Spielern an Magier
1274 *-----------------------------------------------------------------
1275 */
1276static int fehlerhilfe(string str) {
1277 write("Welche Art von Fehler moechtest Du denn melden?\n"
1278 "Fehlfunktionen -> bug\n"
1279 "Ideen/Anregungen -> idee\n"
1280 "Tippfehler/Typos -> typo\n"
1281 "fehlende Details -> detail\n");
1282
1283 return 1;
1284}
1285
Arathorn3437e392016-08-26 22:41:39 +02001286static varargs int ReportError2(string player_input, string error_type)
1287{
1288 if ( stringp(player_input) && sizeof(player_input) &&
1289 player_input != "~q" && player_input != ".." )
1290 {
1291 object obj;
1292 // Eingabe am : aufsplitten, da dieser als Trennzeichen verwendet wird,
1293 // wenn sich die Eingabe auf ein bestimmtes Objekt beziehen soll. Das
1294 // erste Element im Ergebnisarray wird dann als dessen ID aufgefasst,
1295 // nach der weiter unten gesucht wird.
1296 // Zusaetzlich Whitespace drumherum wegschneiden.
1297 string *input_segments = regexplode(player_input,
1298 "[[:blank:]]*:[[:blank:]]*", RE_OMIT_DELIM | RE_PCRE);
1299
1300 // Wenn mind. 2 Segmente erzeugt wurden, soll offenbar ein bestimmtes
1301 // Objekt angesprochen werden, so dass dieses nun gesucht werden kann.
1302 if ( sizeof(input_segments) > 1 )
1303 {
1304 // Findet sich hinter dem ersten : nur ein Leerstring, wird davon
1305 // ausgegangen, dass da keine Meldung kam.
1306 // Fuer seltene Sonderfaelle wie mehrfache ":" wird noch geprueft, ob
1307 // von der rekonstruierten Meldung abzueglich der ":" ein Leerstring
1308 // uebrigbleibt. Wenn ja, war es wohl wirklich eine unbrauchbare
1309 // Meldung, und es wird eine neue angefordert.
1310 if ( input_segments[1] == "" &&
1311 implode(input_segments[1..],"") == "" )
1312 {
1313 _notify("Du hast hinter dem : nix eingegeben, bitte nochmal "
1314 "versuchen.\n", MA_UNKNOWN);
1315 // Eine neue Eingabe wird aber nur angefordert, wenn der aktuelle
1316 // Input ohnehin schon aus einem input_to()-Durchlauf stammte.
1317 // In diesem Fall ist extern_call() wahr.
1318 if ( extern_call() )
1319 input_to("ReportError2", INPUT_PROMPT, "]", error_type);
1320 return 1;
1321 }
1322
1323 // ID kleinschreiben, denn nur eine solche kann von Spielern eingegeben
1324 // werden, um ein Objekt anzusprechen, wir haben aber die Eingabe
1325 // per _unparsed_args(0) geholt, d.h. ggf. mit Grossbuchstaben.
1326 string obnam = lower_case(input_segments[0]);
1327
1328 // Und das Objekt suchen, dabei zuerst im env() schauen. Nur present()
1329 // ginge auch, wuerde aber zuerst im Inv suchen, was aber nicht
1330 // gewuenscht ist.
1331 obj = present(obnam, environment(this_object())) || present(obnam);
1332
1333 // Wenn das Objekt gefunden wird, wird nur der Teil hinter dem ersten
1334 // : rekonstruiert und als Fehlermeldung abgesetzt.
1335 // Gibt es ein solches Objekt nicht, wird der Raum als Fallback
1336 // verwendet. Die Eingabe vor dem : gehoerte dann offenbar zur Meldung
1337 // dazu, und es wird dann spaeter die gesamte Spielereingabe im Original
1338 // als Fehlermeldung abgesetzt.
1339 if ( objectp(obj) )
1340 player_input = implode(input_segments[1..],":");
1341 else
1342 obj = environment(this_object());
1343 }
1344 else
1345 {
1346 // Hat der Spieler keinen : verwendet, verwenden wir das Bezugsobjekt
1347 // oder den aktuellen Raum.
1348 obj = QueryProp(P_REFERENCE_OBJECT);
1349 if (!objectp(obj) || !present(obj))
1350 obj = environment(this_interactive());
1351 }
1352 _notify("Vielen Dank fuer die Hilfe.\n", MA_UNKNOWN);
1353 smart_log(error_type, player_input, obj);
MG Mud User88f12472016-06-24 23:31:02 +02001354 }
Arathorn3437e392016-08-26 22:41:39 +02001355 else
1356 _notify("Eingabe abgebrochen.\n", MA_UNKNOWN);
MG Mud User88f12472016-06-24 23:31:02 +02001357 return 1;
1358}
1359
Arathorn3437e392016-08-26 22:41:39 +02001360static int ReportError(string player_input)
1361{
1362 // ungeparstes Kommando einlesen, um die Meldung unmodifiziert zu erhalten
1363 player_input = _unparsed_args(0);
1364 string pl_msg, error_type;
MG Mud User88f12472016-06-24 23:31:02 +02001365
Arathorn3437e392016-08-26 22:41:39 +02001366 // Anhand des eingegebenen Kommandoverbs wird der Typ der Fehlermeldung
1367 // ermittelt.
1368 switch(query_verb())
1369 {
1370 case "idee":
1371 pl_msg = "Was fuer eine Idee hast Du denn?\n";
1372 error_type = "IDEA";
1373 break;
1374 case "md":
1375 case "detail":
1376 pl_msg = "Fuer welches Detail fehlt denn die Beschreibung?\n";
1377 error_type = "DETAILS";
1378 break;
1379 case "typo":
1380 pl_msg = "Wo ist denn der Tippfehler?\n";
1381 error_type = "TYPO";
1382 break;
1383 case "bug":
1384 pl_msg = "Wie sieht der Fehler denn aus?\n";
1385 error_type = "BUGS";
1386 break;
MG Mud User88f12472016-06-24 23:31:02 +02001387 }
MG Mud User88f12472016-06-24 23:31:02 +02001388
Arathorn3437e392016-08-26 22:41:39 +02001389 // Hat der Spieler etwas eingegeben, wird die Eingabe direkt an die Hilfs-
1390 // funktion weitergereicht. Ansonsten wird eine Meldung ausgegeben und die
1391 // Eingabe manuell per input_to() abgefragt.
1392 if ( stringp(player_input) && sizeof(player_input) )
1393 {
1394 return ReportError2(player_input, error_type);
MG Mud User88f12472016-06-24 23:31:02 +02001395 }
Arathorn3437e392016-08-26 22:41:39 +02001396 ReceiveMsg(pl_msg, MT_NOTIFICATION);
1397 input_to("ReportError2", INPUT_PROMPT, "]", error_type);
MG Mud User88f12472016-06-24 23:31:02 +02001398 return 1;
1399}
1400
1401/** Loggt eine Spielermeldung an Magier.
1402 * Loggt die Spielermeldung in das passende File unter /log/report/ oder im
1403 * vom Magier gewuenschten File. Hierbei werden Fehler, Ideen, MDs und Typos
1404 * in getrennte Files sortiert.
1405 * \param[in] str Spielermeldung
1406 * \param[in] myname Art der Spielermeldung (DETAILS, BUG, TYPO, MD)
1407 * @see md(string), idea(string), bug(string), typo(string)
1408 */
Arathorn3437e392016-08-26 22:41:39 +02001409void smart_log(string myname, string str, object obj)
MG Mud User88f12472016-06-24 23:31:02 +02001410{
MG Mud User88f12472016-06-24 23:31:02 +02001411 mapping err = ([ F_PROG: "unbekannt",
1412 F_LINE: 0,
1413 F_MSG: str,
1414 F_OBJ: obj
1415 ]);
1416
1417 string desc="etwas unbekanntes";
1418 switch(myname) {
1419 case "BUGS":
1420 desc="einen Fehler";
1421 err[F_TYPE]=T_REPORTED_ERR;
1422 break;
1423 case "DETAILS":
1424 desc="ein fehlendes Detail";
1425 err[F_TYPE]=T_REPORTED_MD;
1426 break;
1427 case "IDEA":
1428 desc="eine Idee";
1429 err[F_TYPE]=T_REPORTED_IDEA;
1430 break;
1431 case "TYPO":
1432 desc="einen Typo";
1433 err[F_TYPE]=T_REPORTED_TYPO;
1434 break;
1435 }
1436
1437 // Eintragung in die Fehler-DB
1438 string hashkey = (string)ERRORD->LogReportedError(err);
1439
1440 // ggf. will das Objekte mit noch irgendwas anfangen.
1441 obj->SmartLog(0, myname, str, strftime("%d. %b %Y"));
1442
1443 tell_object(this_object(), break_string( sprintf(
1444 "Du hast an %s erfolgreich %s abgesetzt.\n"
1445 "Die ID der abgesetzten Meldung lautet: %s\n",
1446 (obj->IsRoom() ? "diesem Raum" : obj->name(WEM,1)),desc,
1447 hashkey||"N/A"),78,BS_LEAVE_MY_LFS));
1448}
1449
1450/** Speichert den Spieler und loggt ihn aus (Spielerkommando 'ende').
1451 * Der Spieler wird vollstaendig ausgeloggt, d.h. das Spielerobjekt
1452 * zerstoert.
1453 * \return 1 bei Erfolg, 0 sonst.
1454 * @see disconnect()
1455 */
1456int quit()
1457{
1458 int arg;
1459 SetProp(P_LAST_QUIT,time());
1460 catch(RemoveChannels();publish);
1461 if(!QueryGuest())
1462 {
1463 save_me(0);
1464 tell_object(ME,"Speichere "+QueryProp(P_NAME)+".\n");
1465 }
1466
1467 if (interactive(ME))
1468 call_notify_player_change(0);
1469
1470 remove_living_name();
1471 // EVT_LIB_LOGOUT wird in remove() getriggert.
1472 if(catch(remove();publish)) destruct(ME);
1473 return 1;
1474}
1475
1476/** Wrapper im quit() herum, verhindert 'ende', falls Spieler kaempft.
1477 * \return 0 oder Rueckgabewert von quit()
1478 * @see quit()
1479 */
1480static int new_quit() {
1481 notify_fail("Du bist in Gedanken noch bei Deinem letzten Kampf.\n"+
1482 "Warte noch etwas bevor Du das Spiel verlaesst,\n"+
1483 "damit Du so nicht in RL weitermachst...\n");
1484 if (time()-Query(P_LAST_COMBAT_TIME)<120 && !IS_LEARNING(ME))
1485 return 0;
1486 return quit();
1487}
1488
1489/** Gibt die Infos ueber den Char an den Spieler aus (Spielerkommando 'info').
1490 * \param[in] arg Wenn arg=="short", wird eine Kurzuebersicht ausgegeben.
1491 * \return 1
1492 * @see short_score()
1493 */
1494static int score(string arg) {
1495 string tmp, gender;
1496 int i,sz,val;
1497 mixed ind;
1498 object *enem1, *enem2, *inv;
1499
1500 if (QueryProp(P_GHOST)) {
1501 write("Im ewigen Leben gibt es keine Punkte.\n");
1502 return 1;
1503 }
1504
1505 int plev = LEPMASTER->QueryLevel();
1506
1507 switch(tmp = QueryProp(P_GENDER)) {
1508 case MALE: gender = "maennlich"; break;
1509 case FEMALE: gender = "weiblich"; break;
1510 case NEUTER: gender = "neutral"; break;
1511 default: gender = lower_case(tmp);
1512 }
1513
1514 ind = m_indices(QueryProp(P_ATTRIBUTES));
1515 tmp = "";
1516 foreach(string index: ind) {
1517 string aname;
1518 switch (index) {
1519 case "int": aname = "Intelligenz"; break;
1520 case "con": aname = "Ausdauer"; break;
1521 case "dex": aname = "Geschicklichkeit"; break;
1522 case "str": aname = "Kraft"; break;
1523 default:
1524 if(stringp(index)) aname = capitalize(index);
1525 else aname = "Unbekannt";
1526 }
1527 aname = sprintf("%-18'.'s %2.2d", aname+" ", QueryRealAttribute(index));
1528 if((val = QueryAttributeOffset(index)))
1529 aname += sprintf(" (%s%d)", (val>=0?"+":""), val);
1530 tmp += aname + "\n";
1531 }
1532
1533 printf("- %-'-'68s\n",
1534 TeamPrefix()+capitalize(implode(explode(short()||"","\n"),""))+" ");
1535 if(arg!="short") {
1536 printf("Rasse ............ %-' '18s Abenteuer ........ %d %s\n",
1537 QueryProp(P_RACE), QueryProp(P_QP),
1538 (val = QM->QueryTotalQP()) == QueryProp(P_QP) ? "" : "("+val+")");
1539 printf("Geschlecht ....... %-' '18s Groesse .......... %d cm\n",
1540 gender, QueryProp(P_SIZE));
1541 printf("Stufe ............ %-3.3d %-' '14s Gewicht .......... %d kg\n",
1542 QueryProp(P_LEVEL), (QueryProp(P_LEVEL) < plev ? "("+plev+")" : ""),
1543 QueryProp(P_WEIGHT) / 1000);
1544 printf("Gilde ............ %-' '18s Gildenstufe ...... %d\n",
1545 capitalize(QueryProp(P_GUILD)), QueryProp(P_GUILD_LEVEL));
1546 }
1547 printf("Erfahrung ........ %-' '18s Charakter ........ %-s\n\n",
1548 QueryProp(P_XP)+ " Punkte", al_to_title(QueryProp(P_ALIGN)));
1549 printf("%#-76.2s\n\n", tmp);
1550 printf("Gesundheit ....... %-3.3d %-' '14s Gift ............. %s\n",
1551 QueryProp(P_HP),
1552 (QueryProp(P_HP) == (val = QueryProp(P_MAX_HP)) ? "" : "("+val+")"),
1553 ((val = QueryProp(P_POISON)) ?
1554 (val < 4 ? "leicht" : "gefaehrlich") : "gesund"));
1555 printf("Konzentration .... %-3.3d %-' '14s Vorsicht ......... %s\n",
1556 QueryProp(P_SP),
1557 (QueryProp(P_SP) == (val = QueryProp(P_MAX_SP)) ? "" : "("+val+")"),
1558 ((ind = QueryProp(P_WIMPY)) ? ""+ind : "mutig"));
1559 printf("Todesfolgen....... %-' '18s %s\n",
1560 ((val = death_suffering()) ? ""+((val+9)/10) : "kein Malus"),
1561 (QueryProp(P_WIMPY) && ind=QueryProp(P_WIMPY_DIRECTION))
1562 ? sprintf("Fluchtrichtung ... %O", ind) : "");
1563 printf("%s",
1564 (time()-Query(P_LAST_COMBAT_TIME)<120 && !IS_LEARNING(ME)) ?
1565 "Spiel verlassen .. nicht moeglich\n" : ""
1566 );
1567
1568 if(arg!="short") {
1569 write(break_string(Forschung(), 70));
1570 if(ind=QueryProp(P_AWAY))
1571 printf("Du bist nicht ansprechbar: %O\n",ind);
1572 }
1573
1574 if(sizeof(enem1=((mixed)QueryEnemies())[0])) {
1575 enem2=({});
1576 inv=all_inventory(environment(ME));
1577 foreach(object en: enem1) {
1578 if (member(inv,en)==-1) // Ist unser Feind und ist nicht hier
1579 enem2+=({en});
1580 }
1581 if(sizeof(enem2))
1582 {
1583 write(break_string(
1584 "Du verfolgst " + CountUp(map_objects(enem2, "name", WEN))+".",
1585 78));
1586 }
1587 }
1588 if(arg!="short") show_age();
1589 printf("%-'-'70s\n", "");
1590 return 1;
1591}
1592
1593/** Gibt eine kuerzere Info ueber den Char aus (Spielerkommando punkte|score).
1594 Ruft score("short").
1595 * \param[in] arg UNUSED
1596 * \return 1 bei Erfolg, 0 sonst.
1597 * @see score(string), very_short_score()
1598 */
1599static int short_score(string arg) {
1600 return score("short");
1601}
1602
1603/** Gibt eine Miniinfo ueber LP / KP aus (Spielerkommando: kurzinfo)
1604 * \return 1
1605 * @see score(string), short_score(string)
1606 */
1607static int very_short_score(string arg) {
1608 int lp,mlp,xlp,kp,mkp,xkp;
1609 string bar;
1610
1611 lp=QueryProp(P_HP); mlp=QueryProp(P_MAX_HP);
1612 kp=QueryProp(P_SP); mkp=QueryProp(P_MAX_SP);
1613 if (mlp)
1614 xlp=(lp*40/mlp);
1615 if (mkp)
1616 xkp=(kp*40/mkp);
1617 bar=" . . . . . . . . ";
1618 if (QueryProp(P_NO_ASCII_ART) || arg == "-k")
1619 printf("Gesundheit: %3.3d (%3.3d), Konzentration: %3.3d (%3.3d)\n",
1620 lp, mlp, kp, mkp);
1621 else
1622 printf("Gesundheit: 0 |%'#'40.40s| %3.3d%s\n"+
1623 "Konzentration: 0 |%'#'40.40s| %3.3d%s\n",
1624 (xlp<0?bar:bar[xlp..]),lp,(lp==mlp?"":sprintf(" (%d)",mlp)),
1625 (xkp<0?bar:bar[xkp..]),kp,(kp==mkp?"":sprintf(" (%d)",mkp))
1626 );
1627 return 1;
1628}
1629
1630/** Gibt eine Manpage/Hilfeseite an den Spieler aus.
1631 Beruecksichtigt hierbei die Synonymliste aus dir/.synonym, um die richtige
1632 Manpage auszugeben.
1633 * \param[in] dir Verzeichnis der gewuenschten Manpage
1634 * \param[in] page Name der gewuenschten Manpage
1635 * \return String der gewuenschten Manpage
1636 */
1637static string getmanpage(string dir, string page)
1638{
1639 string text, *syn;
1640 int i;
1641
1642 if (dir[<1] != '/')
1643 dir += "/";
1644
1645 if ((text=read_file(dir+page)) && sizeof(text))
1646 return text;
1647
1648 if (text = read_file(dir+".synonym")) {
1649 syn = regexplode(text, "([ \t][ \t]*|\n)");
1650 if ((i=member(syn, page))!=-1)
1651 return read_file(dir+syn[i+2]);
1652 }
1653 return 0;
1654}
1655
1656/** Gibt eine Hilfeseite an den Spieler aus (Spielerkommando hilfe|man).
1657 * Die Hilfeseite wird in div. Verzeichnissen gesucht (je nach Gilde,
1658 * Magier-/Spielerstatus).
1659 * \param[in] str Name der gewuenschten Hilfeseite.
1660 * \return 1
1661 */
1662static int help(string str) {
1663 string verb, rest, text, gilde;
1664 mixed found;
1665
1666 found=0;
1667 text = "";
1668 if (str) {
1669 str = implode( explode(str, ".." ), "");
1670
1671 if ( sscanf( str, "gilde %s %s", gilde, rest)==2)
1672 str=rest;
1673 else
1674 gilde=QueryProp(P_GUILD);
1675 if (!gilde) gilde="abenteurer";
1676
1677 if ( sscanf( str, "%s %s",verb,rest )==2 ) str = verb;
1678
1679 if ((IS_LEARNER(PL)) ) {
1680 if (rest = getmanpage("/doc/wiz/",str)) {
1681 found = 1;
1682 text += rest;
1683 }
1684 else if (rest = getmanpage("/doc/mcmd/", str)) {
1685 found = 1;
1686 text += rest;
1687 }
1688 }
1689
1690 if ((IS_SEER(PL)) /*&& !found*/ ) {
1691 if (rest = getmanpage("/doc/scmd/",str)) {
1692 if (found)
1693 text += "\n--------------------\n";
1694 found = 1;
1695 text += rest;
1696 }
1697 }
1698
1699 if (rest = getmanpage("/doc/g."+gilde+"/",str)) {
1700 if (found)
1701 text += "\n--------------------\n";
1702 found = 1;
1703 text += rest;
1704 } else {
1705 if (rest = getmanpage("/doc/help/",str)) {
1706 if (found)
1707 text += "\n--------------------\n";
1708 found = 1;
1709 text += rest;
1710 }
1711 else if (rest = getmanpage("/doc/pcmd/",str)) {
1712 if (found)
1713 text += "\n--------------------\n";
1714 found = 1;
1715 text += rest;
1716 }
1717 else if (rest = getmanpage("/doc/REGELN/",str)) {
1718 if (found)
1719 text += "\n--------------------\n";
1720 found = 1;
1721 text += rest;
1722 }
1723 }
1724
1725 if (!found)
1726 text = "Dazu ist keine Hilfe verfuegbar.\n";
1727
1728 More(text,0);
1729 return 1;
1730 }
1731 if (IS_LEARNER(PL))
1732 text = read_file("/doc/hilfe.magier");
1733 else if (IS_SEER(PL))
1734 text = read_file("/doc/hilfe.seher");
1735
1736 More(text + read_file("/doc/hilfe.spieler"), 0);
1737 return 1;
1738}
1739
1740/** Ermittelt angebene Optionen fuer das Spielerkommando 'wer'.
1741 * \param[in] str vom Spieler spezifizierter String von Filteroptionen: -k,
1742 * -v, -a, -s (+ Langformen).
1743 * \return Array von Spieleroptionen als veroderte Int-Flags und Rest der
1744 * Spielereingabe ohne die Optionen.
1745 */
1746static mixed filter_who_options(string str)
1747{
1748 string* opt, *ans;
1749 int i,len,res;
1750
1751 opt = explode(str," "); len=sizeof(opt);
1752 ans = ({});
1753 res = 0;
1754 for(i=0;i<len;i++)
1755 switch(opt[i]){
1756 case "-k":
1757 case "-kurz":
1758 res |= WHO_SHORT; break;
1759 case "-v":
1760 case "-vertikal":
1761 res |= WHO_VERTICAL; break;
1762 case "-alphabetisch":
1763 case "-a":
1764 case "-alpha":
1765 res |= WHO_ALPHA; break;
1766 case "-s":
1767 case "-spieler":
1768 res |= WHO_PLAYER_VIEW; break;
1769 default:
1770 return ({ res, implode(opt[i..]," ") });
1771 }
1772 return ({ res, 0 });
1773
1774}
1775
1776/** Spielerkommando 'wer', fragt /obj/werliste ab.
1777 * \param[in] str Spielereingabe mit Optionen fuer wer.
1778 * \return 1
1779 */
1780static int who(string str) {
1781 int i,shrt;
1782 string ret;
1783 mixed ans;
1784
1785 if ((str=_unparsed_args())&&str[0..0]!="-") {
1786 ans = filter_who_options(str);
1787 shrt = ans[0];
1788 str = ans[1];
1789 if (!shrt) {
1790 if (ret=INETD->_send_udp(str,
1791 ([ REQUEST: "who", SENDER: getuid(ME) ]), 1 ))
1792 write(ret);
1793 else
1794 write("Anfrage abgeschickt.\n");
1795 return 1;
1796 }
1797 }
1798 if (str) i=(member(str,'o')>0); else i=0;
1799 if (sizeof(str)>1 && str[0] == '-') str = str[1..1];
1800 More(implode( "/obj/werliste"->QueryWhoListe(
1801 IS_LEARNER(ME) && QueryProp(P_WANTS_TO_LEARN),shrt,0,str,i),"\n"),0);
1802 return 1;
1803}
1804
1805/** Spielerkommando 'kwer', fragt /obj/werliste ab.
1806 * \param[in] str Spielereingabe mit Optionen fuer wer.
1807 * \return 1
1808 */
1809static int kwho(string str)
1810{
1811 int shrt;
1812 mixed res;
1813
1814 if(str) {
1815 res = filter_who_options(str);
1816 shrt = res[0];
1817 str = res[1];
1818 }
1819 More(implode( "/obj/werliste"->QueryWhoListe(
1820 IS_LEARNER(ME) && QueryProp(P_WANTS_TO_LEARN), shrt|WHO_SHORT ,0,str),
1821 "\n")+"\n\n",0);
1822 return 1;
1823}
1824
1825/** Spielerkommando 'kkwer', gibt eine einfache Liste der Anwesenden aus.
1826 Filtert unsichtbare Spieler aus, falls SPielerobjekt kein Magier ist.
1827 * \param[in] str Spielereingabe mit Optionen fuer wer.
1828 * \return 1
1829 */
1830static varargs int kkwho(string str) {
1831 object *obs;
1832 string *namen;
1833
1834 obs=filter_users(str);
1835 namen=({});
1836 if (IS_LEARNER(this_player())) {
1837 foreach(object ob: obs) {
1838 if (environment(ob))
1839 namen+=({capitalize(geteuid(ob))});
1840 }
1841 }
1842 else {
1843 foreach(object ob: obs) {
1844 if (!ob->QueryProp(P_INVIS) && environment(ob))
1845 namen+=({capitalize(geteuid(ob))});
1846 }
1847 }
1848 if (sizeof(namen))
1849 write(break_string(CountUp(sort_array(namen,#'>),", ", ", ")+".",75));
1850 else
1851 write("Keine passenden Spieler gefunden.\n");
1852
1853 return 1;
1854}
1855
1856/** Spielerkommando 'toete'.
1857 * Prueft auf Geist, Gast, toten HC-Spieler, Waffe/freie Hand. Versucht einen
1858 * Gegner zu finden und ruft dann Kill(string).
1859 * \param[in] str Spielereingabe
1860 * \return 1 oder 0, falls kein potentieller Gegner bei 'toete alle' gefunden
1861 * wird.
1862 */
1863static int kill(string str) {
1864 object eob,wob;
1865
1866 if (QueryProp(P_GHOST))
1867 {
1868 write("Das kannst Du in Deinem immateriellen Zustand nicht.\n");
1869 return 1;
1870 }
1871
1872 if(hc_play>1)
1873 {
1874 write("DAS HAST DU HINTER DIR.\n");
1875 return 1;
1876 }
1877
1878 if (QueryGuest())
1879 {
1880 write("Du bist doch nur Gast hier.\n");
1881 return 1;
1882 }
1883 if (!str || str == "") {
1884 write("WEN willst Du toeten?\n");
1885 return 1;
1886 }
1887 if( !QueryProp(P_WEAPON) && QueryProp(P_FREE_HANDS)==0 ) {
1888 write(
1889 "Dazu solltest Du eine Waffe gezueckt oder eine Hand frei haben.\n");
1890 return 1;
1891 }
1892 str=lower_case(str);
1893 if (str=="alle") {
1894 object livs;
1895 livs=filter(all_inventory(environment(PL)),
1896 function int (object ob) {
1897 if (living(ob) && !query_once_interactive(ob)
1898 && !ob->QueryProp(P_INVIS)
1899 && !ob->QueryProp(P_NO_GLOBAL_ATTACK)
1900 && !ob->QueryProp(P_FRIEND))
1901 {
1902 Kill(ob);
1903 return 1;
1904 }
1905 return 0;
1906 } );
1907 // wenn Gegner gefunden, raus, ansonsten kommt die Fehlermeldung unten.
1908 if (sizeof(livs)) return 1;
1909 }
1910 else {
1911 int i=1;
1912 while(objectp(eob = present(str,i++,environment(PL)))) {
1913 if (living(eob) && !eob->QueryProp(P_INVIS))
1914 break;
1915 else
1916 eob=0;
1917 }
1918 }
1919 if (!objectp(eob)) {
1920 // per write und return 1 ist hier mal ok, weil dies Kommando im Spieler
1921 // eh zuletzt in der Kommandokette ausgefuehrt wird und per return 0 eh
1922 // kein anderes mehr zum Zug kommt.
1923 write("Du siehst hier kein derartiges Wesen!\n");
1924 return 1;
1925 }
1926 else if (eob == PL) {
1927 write("Selbstmord ist keine Loesung!\n");
1928 return 1;
1929 }
1930
1931 /* Kill him */
1932 Kill(eob);
1933 return 1;
1934}
1935
1936/** Spielerkommando 'stop'.
1937 * Loescht die Gegnerliste, sofern man nicht InFight() ist.
1938 * \param[in] str Spielereingabe, wird ignoriert.
1939 * \return 1
1940 */
1941static int stop( string str )
1942{
1943 if ( InFight() ){
1944 write( "Das geht nicht mitten im Kampf.\n" );
1945 return 1;
1946 }
1947
1948 if ( !str ){
1949 StopHuntingMode();
1950 write( "Ok.\n" );
1951 return 1;
1952 }
1953
1954 if ( !StopHuntID(str) )
1955 write( "So jemanden verfolgst Du nicht!\n" );
1956
1957 return 1;
1958}
1959
1960/** Spielerkommando fuers emoten ':'.
1961 * \param[in] str Spielereingabe
1962 * \param[in] genitiv Genetivflag
1963 * \return 1 oder 0, falls Spieler nicht emoten kann (kein CAN_EMOTE Flag in
1964 * P_CAN_FLAGS).
1965 */
1966int emote(string str,int genitiv)
1967{
1968 string *commands,message,verb;
1969 object living;
1970 int i,size;
1971
1972 if (!(Query(P_CAN_FLAGS)&CAN_EMOTE)) return 0;
1973 if (query_verb()[0]==';') genitiv=1;
1974 if (query_verb()[0]==':'||query_verb()[0]==';')
1975 verb=query_verb()[1..]+" ";
1976 else
1977 verb="";
1978 str=this_player()->_unparsed_args();
1979 commands=explode(verb+(str||""),"#");
1980 message=break_string((IS_SEER(ME) ? "" : ">")
1981 +capitalize(genitiv ? name(WESSEN) :
1982 name())
1983 +" "+commands[0],78);
1984 size=sizeof(commands);
1985 if(size>=3)
1986 {
1987 living=find_living(lower_case(commands[1]));
1988 if(!living || environment(living)!=environment() ||
1989 (living->QueryProp(P_INVIS)) && !IS_LEARNER(ME))
1990 {
1991 write(capitalize(commands[1])+" sehe ich hier nicht!\n");
1992 return 1;
1993 }
1994 if(living!=this_object())
1995 tell_object(living,break_string((IS_SEER(this_player()) ? "" : ">")
1996 +capitalize(genitiv ?
1997 this_player()->name(WESSEN) :
1998 this_player()->name())
1999 +" "+commands[2],78));
2000 }
2001 if(size>=4)
2002 write(break_string(commands[3],78));
2003 else
2004 write(message);
2005 tell_room(environment(),message,({this_object(),living}));
2006 return 1;
2007}
2008
2009/** Spielerkommando fuers remoten 'r:'.
2010 * \param[in] str Spielereingabe
2011 * \param[in] flag Genetivflag
2012 * \return 1 oder 0, falls Spieler nicht emoten kann (kein CAN_REMOTE Flag in
2013 * P_CAN_FLAGS).
2014 */
2015static int remote(string str, int flag)
2016{
2017 int m;
2018 string tmp, dest;
2019 string *exstr;
2020 object destpl;
2021
2022 if ( !(Query(P_CAN_FLAGS) & CAN_REMOTE) )
2023 return 0;
2024
2025 if ( !(str=_unparsed_args()) ||
2026 sizeof( (exstr=explode(str," ")) - ({""}) ) <= 1 ){
2027 write("Was willst Du zu wem `emoten`?\n");
2028 return 1;
2029 }
2030
2031 dest = lower_case(exstr[0]);
2032
2033 if( !(destpl=find_player( dest ) ) ||
2034 (destpl->QueryProp(P_INVIS) && !IS_LEARNER(ME)) ){
2035 write("Einen solchen Spieler gibt es derzeit nicht.\n");
2036 return 1;
2037 }
2038
2039 tmp = implode( exstr[1..], " " );
2040
2041 tmp = regreplace( tmp, "(^|[^#])#($|[^#])", "\\1aus der Ferne\\2", 1 );
2042 tmp = regreplace( tmp, "(^|[^\\^])\\^($|[^\\^])", "\\1in der Ferne\\2", 1 );
2043 tmp = regreplace( tmp, "##", "#", 1 );
2044 tmp = regreplace( tmp, "\\^\\^", "^", 1 );
2045
2046 if ( strstr( tmp, "aus der Ferne" ) == -1
2047 && strstr( tmp, "in der Ferne" ) == -1 )
2048 tmp += " aus der Ferne";
2049
2050 if ( QueryProp(P_INVIS) && IS_LEARNER(destpl) ){
2051 str = "(" + capitalize(getuid(ME));
2052 if ( flag )
2053 str += member( "sxz", str[<1] ) == -1 ? "s" : "'";
2054 str += ")";
2055 }
2056 else
2057 str = (flag ? capitalize(name(WESSEN)) : capitalize(name(WER)));
2058
2059 str += " " + tmp + (member( ".?!", tmp[<1] ) == -1 ? "." : "") + "\n";
2060
2061 m = destpl->ReceiveMsg(str, MT_COMM|MT_FAR, MA_EMOTE,0, ME);
2062 switch(m)
2063 {
2064 case MSG_DELIVERED:
Bugfix70d66a52017-03-06 14:27:35 +01002065 // Als origin muss der Empfaenger uebergeben werden, sonst kann die
2066 // Meldung in der tmhist nicht richtig zugeordnet werden.
Zesstra4eb67dc2016-12-17 19:56:31 +01002067 ReceiveMsg(capitalize(destpl->name()) + "->" + str, MT_COMM|MT_FAR,
Bugfix70d66a52017-03-06 14:27:35 +01002068 MA_EMOTE, 0, destpl);
MG Mud User88f12472016-06-24 23:31:02 +02002069 break;
2070 case MSG_BUFFERED:
2071 write( capitalize(destpl->name(WER) + " ist gerade beschaeftigt.\n") );
2072 break;
2073 case MSG_IGNORED:
2074 write( capitalize(destpl->name(WER) + " ignoriert Dich.\n") );
2075 break;
2076 case MSG_VERB_IGN:
2077 case MSG_MUD_IGN:
2078 write( capitalize(destpl->name(WER) + " ignoriert Deine Meldung.\n") );
2079 break;
2080 default:
2081 write( capitalize(destpl->name(WER) + " kann Dich gerade nicht "
2082 "wahrnehmen.\n") );
2083 }
2084 return 1;
2085}
2086
2087/** Spielerkommando fuers emoten im Genitiv ';'.
2088 * Ruft emote(string,int) auf.
2089 */
2090static int gemote(string str)
2091{
2092 return emote(str, 1);
2093}
2094
2095/** Spielerkommando fuers remoten im Genitiv 'r;'.
2096 * Ruft remote(string, int) auf.
2097 */
2098static int gremote(string str)
2099{
2100 return remote(str, 1);
2101}
2102
2103static void load_auto_objects(mapping map_ldfied);
2104
2105private void InitPlayer();
2106private void InitPlayer2();
2107private void InitPlayer3();
2108
2109/** Gibt eine Zufallszahl um P_AVERAGE_SIZE herum zurueck.
2110 * \return Zufaellige Groesse.
2111 */
2112private int RandomSize()
2113{
2114 return (100+random(13)-random(13)+random(13)-random(13))*
2115 (QueryProp(P_AVERAGE_SIZE)||170)/100;
2116}
2117
2118/** Setzt bestimmte Props im Spieler, falls diese nicht gesetzt sind oder
2119 * loescht obsolete Props. Repariert bestimmte Datenstrukturen im Spieler.
2120 * Wird von start_player() nach Laden des Savefiles gerufen.
2121 * Momentan wird z.B. die Groesse gesetzt, falls sie bisher 0 ist und die
2122 * Skills des Spielers initialisiert bzw. repariert oder auf die aktuellste
2123 * Version des Skillsystems
2124 * Ruft ggf. InitSkills() und FixSkills().
2125 * @param[in] newflag Gibt an, ob es ein neuerstellter Spieler ist.
2126 * @sa start_player(), InitSkills(), FixSkills()
2127 */
2128private void updates_after_restore(int newflag) {
2129
2130 // Seher duerfen die Fluchtrichtung uebermitteln lassen.
2131 // Eigentlich koennte es Merlin machen. Dummerweise gibt es ja auch alte
2132 // Seher und dann kann es gleiche fuer alle hier gemacht werden. (Ob der
2133 // Code jemals rauskann?)
2134 //TODO: Irgendwann alle Seher korrigieren und Code nach Merlin schieben...
2135 if (IS_SEER(ME))
2136 SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS) | CAN_REPORT_WIMPY_DIR);
2137
2138 // ggf. Invis-Eigenschaft aus dem Loginobjekt abrufen (Invislogin), koennte
2139 // ja anders als aus Savefile sein. Gesetztes P_INVIS aus diesem aber
2140 // beibehalten.
2141 if (IS_LEARNER(ME) && !QueryProp(P_INVIS)
2142// && load_name(previous_object()) == "/secure/login"
2143 )
2144 {
2145 SetProp(P_INVIS, previous_object()->query_invis());
2146 if (QueryProp(P_INVIS))
2147 tell_object(ME, "DU BIST UNSICHTBAR!\n" );
2148 }
2149 "*"::updates_after_restore(newflag);
2150
2151 attributes::UpdateAttributes();
2152
2153 int size=Query(P_SIZE);
2154 if (!size) size=RandomSize();
2155 while(size==QueryProp(P_AVERAGE_SIZE))
2156 size=RandomSize();
2157 Set(P_SIZE,size);
2158
2159 // Prop wird nicht mehr genutzt. TODO: irgendwann entfernen.
2160 Set(P_SECOND_LIST, SAVE, F_MODE_AD);
2161 Set(P_SECOND_LIST, 0, F_VALUE);
2162}
2163
2164
2165/** Setzt den HC-Modus.
2166 */
2167varargs nomask void set_hc_play(string str,int val)
2168{
2169 string str1;
2170
2171 str1 = explode( object_name(previous_object()), "#" )[0];
2172
2173 if ( str1 != "/secure/login" &&
2174 previous_object()!=this_object() &&
2175 extern_call() &&
2176 (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
2177 capitalize(geteuid(ME)) != str ||
2178 geteuid(ME) != geteuid(previous_object())) ){
2179 write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
2180 return;
2181 }
2182
2183 hc_play=val;
2184}
2185
2186/** gibt den HC-Modus zurueck.
2187 */
2188nomask int query_hc_play()
2189{
2190 return hc_play;
2191}
2192
2193/** Initialisiert und aktiviert das Spielerobjekt.
2194 * Kann nur von /secure/login oder /secure/master gerufen werden.
2195 * Startet Telnet Negotiation, laedt Savefile, setzt einige Props.
2196 * Ruft updates_after_restore(), um div. Daten zu aktualisieren/reparieren.
2197 * Ruft create() aus /std/player/potion.c.
2198 * Bei neuem Spieler wird der entsprechende Event ausgeloest die Attribute
2199 * auf die Startwerte gesetzt.
2200 * Prueft Zweitiemarkierungen.
2201 * Ruft InitPlayer().
2202 * @param[in] str Name des Charakters, der geladen werden soll.
2203 * @param[in] ip textuelle Repraesentation der IP-Adresse des Spielers.
2204 * @return 1 bei Erfolg, 0 sonst.
2205 * @todo Div. Reparaturen/Updates nach updates_after_restore() auslagern.
2206 */
2207varargs nomask int start_player( string str, string ip )
2208{
2209 mixed second;
2210 int newflag; /* could player be restored? */
2211 string str1;
2212
2213 call_out( "disconnect", 600 );
2214
2215 str1 = explode( object_name(previous_object()), "#" )[0];
2216
2217 if ( str1 != "/secure/login" &&
2218 str1 != "/secure/master" &&
2219 (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
2220 capitalize(geteuid(ME)) != str ||
2221 geteuid(ME) != geteuid(previous_object())) ){
2222 write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
2223 destruct(ME);
2224 return 0;
2225 }
2226
2227 /* try to restore player. If it doesn't exist, set the new flag */
Zesstraf3f22662017-01-30 15:54:29 +01002228 newflag = !restore_object( SAVEPATH + lower_case(str)[0..0] + "/"
MG Mud User88f12472016-06-24 23:31:02 +02002229 +lower_case(str) );
2230
2231 updates_after_restore(newflag);
2232
2233 if ( query_once_interactive(ME) )
2234 {
2235 // Telnet-Negotiations durchfuehren, aber nur die grundlegenden aus
2236 // telnetneg. Alle anderen sollten erst spaeter, nach vollstaendiger
2237 // Initialisierung gemacht werden.
2238 telnetneg::startup_telnet_negs();
2239 modify_prompt();
2240 Set( P_LAST_LOGIN, time() );
2241 }
2242
2243 Set( P_WANTS_TO_LEARN, 1 ); // 1 sollte der default sein !!!
2244 Set( P_WANTS_TO_LEARN, PROTECTED, F_MODE_AS );
2245 // Eingefuegt 18.11.99, kann nach einem Jahr wieder raus:
2246 Set( P_TESTPLAYER, PROTECTED, F_MODE_AS );
2247
2248 if ( IS_LEARNER(ME) )
2249 SetProp( P_CAN_FLAGS, QueryProp(P_CAN_FLAGS)|CAN_REMOTE );
2250
2251 Set( P_NAME, str );
2252 Set( P_NAME, SECURED, F_MODE_AS );
2253
2254 if ( !QueryProp(P_NEEDED_QP) )
2255 SetProp( P_NEEDED_QP, REQ_QP );
2256
2257 Set( P_NEEDED_QP, NOSETMETHOD, F_SET_METHOD );
2258 Set( P_NEEDED_QP, SAVE|SECURED, F_MODE_AS );
2259
2260 /* autosave the player after 500 heartbeats */
2261 time_to_save = age + 500;
2262 potion::create(); /* DO IT HERE AFTER THE RESTORE !! */
2263
2264 AddId( getuid() );
2265 SetProp( P_AC, 0 );
2266 SetProp( P_WEAPON, 0 );
2267
2268 /* Set some things which wont be set when all is OK */
2269 SetProp( P_MAX_HP, (QueryAttribute(A_CON) * 8 + 42 ));
2270 SetProp( P_MAX_SP, (QueryAttribute(A_INT) * 8 + 42 ));
2271
2272 catch( bb = "/secure/bbmaster"->query_bb() );
2273
2274 /* If this is a new character, we call the adventurers guild to get
2275 * our first title !
2276 */
2277 if ( newflag ) {
2278 if ( QueryGuest())
2279 SetProp( P_TITLE, "ueberkommt das "MUDNAME" ..." );
2280
2281 Set( P_LEVEL, -1 );
2282 SetProp( P_ATTRIBUTES, ([ A_STR:1, A_CON:1, A_INT:1, A_DEX:1 ]) );
2283 SetProp( P_HP, QueryProp(P_MAX_HP) );
2284
2285 // Event ausloesen
2286 EVENTD->TriggerEvent(EVT_LIB_PLAYER_CREATION, ([
2287 E_OBJECT: ME,
2288 E_PLNAME: getuid(ME) ]) );
2289 }
2290
2291 InitPlayer();
2292
2293 // Padreic 01.02.1999
Zesstra85576452017-01-30 15:43:21 +01002294 if ( !IS_LEARNER(ME) && (second = QueryProp(P_SECOND)) )
2295 {
2296 if ( stringp(second) && lower_case(second)[0..3] == "von " )
2297 {
MG Mud User88f12472016-06-24 23:31:02 +02002298 second = lower_case(second[4..]);
2299 SetProp( P_SECOND, second );
2300 }
2301
2302 if ( !stringp(second ) ||
Zesstra85576452017-01-30 15:43:21 +01002303 !master()->find_userinfo(second))
2304 {
2305 // Wenns nur an Gross-/Kleinschreibung liegt, wird automatisch
2306 // korrigiert.
MG Mud User88f12472016-06-24 23:31:02 +02002307 if ( stringp(second) &&
Zesstra85576452017-01-30 15:43:21 +01002308 master()->find_userinfo(lower_case(second)))
2309 {
MG Mud User88f12472016-06-24 23:31:02 +02002310 SetProp( P_SECOND, lower_case(second) );
2311 log_file( "WRONG_SECOND",
2312 sprintf( "%s: %s: P_SECOND = %O -> Automatisch "
2313 "korrigiert,\n",
2314 dtime(time()), object_name(), second ) );
2315 }
2316 else {
2317 tell_object( ME,
2318 "*\n*\n* Deine Zweitiemarkierung ist ungueltig, "
2319 "bitte aendere diese und sprich im\n* Zweifel "
2320 "bitte einen Erzmagier an.\n*\n*\n" );
2321
2322 log_file( "WRONG_SECOND",
2323 sprintf( "%s: %s: P_SECOND = %O\n",
2324 dtime(time()), object_name(), second ) );
2325 // ein bisschen deutlicher auffordern.. Padreic 08.04.1999
2326 move( "/d/gebirge/room/zwafflad", M_GO );
2327 }
2328 }
2329 }
2330 return(0);
2331}
2332
2333/** Letzte Phase der Spielerinitialisierung beim Laden des Charakters.
2334 * Ruft enable_commands(), aktiviert den Heartbeat und aktiviert die
2335 * Kommandos aus den geerbten command.c, put_and_get.c, team.c, soul.c,
2336 * guide.c, setzt den Living Name.
2337 * Registriert UseSpell() als Catchall-Kommando.
2338 * Laesst den Spieler ggf. einen Level per /std/gilde aufsteigen, ruft
2339 * call_notify_player_change(), loest Login-Event aus.
2340 * Gibt Willkommenstexte, News und neue Mails aus.
2341 * Findet den Startraum und bewegt den Spieler dorthin.
2342 * Ruft FinalSetup() (aus den Rassenshells).
2343 * Begrenzt Geldmenge im Spieler (wegen Bankzweities) nach Tragkraft und
2344 * erstattet Geld bei Reboot.
2345 * Ruft set_is_wizard().
2346 * Startet Clonen der Autoloader.
2347 * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
2348 */
2349private void InitPlayer4()
2350{
2351 int num, str, ski;
2352 string err, called_from_ip;
2353 mixed start_place;
2354 object mon;
2355
2356 enable_commands();
2357 set_heart_beat(1);
2358 command::initialize();
2359 add_put_and_get_commands();
2360 add_team_commands();
2361 add_soul_commands();
2362 add_guide_commands();
2363 add_action( "UseSpell", "", 1 );
2364 set_living_name( getuid() );
2365 while ( remove_call_out("disconnect") != -1 )
2366 ;
2367
2368 if ( QueryProp(P_LEVEL) == -1 )
2369 {
2370 catch( "/std/gilde"->try_player_advance(this_object()) ;publish );
2371 }
2372
2373 if ( interactive(ME) )
2374 call_notify_player_change(1);
2375
2376 if ( interactive(this_object()) ) {
2377 cat( "/etc/NEWS" );
2378
2379 NewbieIntroMsg();
2380
2381 if ( QueryProp(P_INVIS) && !IS_WIZARD(ME) )
2382 SetProp( P_INVIS, 0 );
2383
2384 catch( num = "secure/mailer"->FingerMail(getuid());publish );
2385
2386 if ( num )
2387 write( "Du hast " + num + " neue" +
2388 (num == 1 ? "n Brief" : " Briefe")+" im Postamt liegen.\n" );
2389
2390 catch( RegisterChannels();publish );
2391
2392 if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
2393 query_ip_number(ME) != called_from_ip ){
2394 string tmp;
2395
2396 if ( stringp(tmp = query_ip_name(called_from_ip)) &&
2397 tmp != called_from_ip )
2398 tmp = " [" + tmp + "]";
2399 else
2400 tmp = "";
2401
2402 write( "Das letzte Mal kamst Du von " + called_from_ip
2403 + tmp + ".\n" );
2404 }
2405
2406 Set( P_CALLED_FROM_IP, query_ip_number(ME) );
2407 }
2408
2409 if ( !stringp(default_home) || default_home == "" )
2410 default_home = "/gilden/abenteurer";
2411
2412 if ( IS_SEER(ME) && !IS_LEARNER(ME) )
2413 catch( start_place = HAUSVERWALTER->FindeHaus(getuid(ME));publish );
2414 // wenn der Spieler noch ganz frisch ist und noch wenig Stufenpunkte
2415 // gekriegt hat und das Tutorial noch nicht gemacht hat, startet er im
2416 // Tutorial.
2417 else if (QueryProp(P_LEP) <= 120
2418 && QM->HasMiniQuest(this_object(),
2419 "/d/anfaenger/ennox/tutorial/npcs/verkaeufer") != 1)
2420 start_place = "/room/welcome/"+ getuid(this_object());
2421 else
2422 start_place = 0;
2423
2424 if ( !start_place )
2425 start_place = QueryProp(P_START_HOME);
2426
2427 if( objectp(start_place) || (stringp(start_place) && start_place != "" ) ){
2428 if ((err = catch(move( start_place, M_GO|M_SILENT|M_NO_SHOW );publish))
2429 || !environment() )
2430 err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
2431 }
2432 else
2433 err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
2434
2435 if ( err )
2436 catch(move( "/gilden/abenteurer", M_GO|M_SILENT|M_NO_SHOW );publish);
2437
2438 // Die Shell muss FinalSetup() nicht implementieren. Daher Callother
2439 catch( ME->FinalSetup();publish );
2440
2441 // Login-event ausloesen
2442 EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
2443 E_OBJECT: ME,
2444 E_PLNAME: getuid(ME),
2445 E_ENVIRONMENT: environment() ]) );
2446
2447 // erst jetzt GMCP freigeben und zu verhandeln.
2448 gmcp::startup_telnet_negs();
2449
2450 // Schonmal sichern, falls ein Bug (Evalcost...) dazwischen kommen sollte.
2451 autoload_rest = autoload;
2452
2453 // Um Geld-Xties mit illegal viel Geld zu vermeiden, kommt ein Check:
2454 if ( !IS_LEARNER(ME) && !Query(P_TESTPLAYER) &&
2455 mon = present( "\ngeld", ME ) ){
2456 // maximale Kraft, die der Spieler haette haben koennen
2457 str = QueryAttribute(A_STR) + 4;
2458 if ( Query(P_FROG) )
2459 str += 30;
2460 if ( str < 1 )
2461 str = QueryRealAttribute(A_STR) + 4;
2462 if ( str > 30 )
2463 str = 30;
2464
2465 // Trageskill beachten
2466 ski = to_int(UseSkill( SK_CARRY, ([SI_SKILLARG : str ]) ));
2467 if ( !intp(ski) )
2468 ski = 0;
2469
2470 // Wieviel konnte der Spieler insgesamt maximal tragen?
2471 num = 9200 + str * 800 + ski;
2472 if ( num < 3000 )
2473 num = 3000;
2474
2475 // Verdoppeln fuer einen guten Container und nochmal 20% draufschlagen
2476 // zur Sicherheit. Das Ganze danach *4, um die maximale Anzahl Muenzen
2477 // zu erhalten.
2478 num = (int) (num * 8.8);
2479
2480 // Geld, das zuviel ist, auf den Maximalwert kuerzen.
2481 // Zur Sicherheit wird mitgeloggt. Falls ein Spieler sich (zu recht)
2482 // beschwert, kann man immer noch wieder die Summe korrigieren ;-)
2483 if ( (str = mon->QueryProp(P_AMOUNT)) > num ){
2484 mon->SetProp( P_AMOUNT, num );
2485 log_file( "ZUVIEL_GELD", sprintf( "%s: %s hatte %d Muenzen bei "
2486 "sich. Korrigiert auf %d.\n ",
2487 ctime(time())[4..],
2488 capitalize(getuid(ME)),
2489 str, num ) );
2490 }
2491 }
2492 int entschaedigung = QueryProp(P_CARRIED_VALUE);
2493 if ( entschaedigung > 0 )
2494 {
2495 write( "Du findest " + entschaedigung +
2496 " Muenzen, die Du beim letzten Mal verloren hast.\n" );
2497
2498 if ( MayAddWeight( entschaedigung / 4 ) ){
2499 write( "Weil Du nicht mehr soviel tragen kannst, spendest Du den "+
2500 "Rest der Zentralbank.\n" );
2501
2502 num = (QueryProp(P_MAX_WEIGHT) - query_weight_contents()) * 4;
2503 entschaedigung = (num < 0) ? 0 : num;
2504 }
2505
2506 AddMoney( entschaedigung );
2507 SetProp(P_CARRIED_VALUE,0);
2508 }
2509
2510 if ( !QueryProp(P_INVIS) )
2511 say( capitalize(name(WER)) + " betritt diese Welt.\n" );
2512 else
2513 write( "DU BIST UNSICHTBAR!\n\n" );
2514#if __EFUN_DEFINED__(set_is_wizard)
2515 if ( IS_WIZARD(getuid(ME)) )
2516 set_is_wizard( ME, 1 );
2517 else
2518 set_is_wizard( ME, 0 );
2519#endif
2520 if ( query_once_interactive(ME) )
2521 ListAwaited();
2522
2523 // Autoloader werden ganz zum Schluss geclont, da das bis zum bitteren
2524 // (Evalcost-)Ende geschieht und danach u.U. keine Rechenzeit fuer
2525 // andere Aktionen mehr ueber ist
2526 load_auto_objects( autoload );
2527}
2528
2529/** Setzt die Spielerinitialisierung nach start_player() fort.
2530 * Prueft den Wert der vom Spieler getragenen Sachen (fuer Entschaedigung
2531 * nach Reboot).
2532 * Setzt div. Properties des "Spielerkoerpers", die eigentlich gespeichert
2533 * werden, nach einem Reboot zurueck.
2534 * Fragt ggf. nach eMail-Adresse und uebergibt per input_to an
2535 * InitPlayer2().
2536 * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
2537*/
2538private void InitPlayer()
2539{
2540 string mailaddr;
2541
2542 // wenn es einen Crash gab, sollen Spieler nicht noch extra bestraft werden
2543 if ( file_time( "/save/" + getuid()[0..0] + "/" + getuid() + ".o" )
2544 < last_reboot_time() ){
2545 SetProp( P_FOOD, 0 );
2546 SetProp( P_DRINK, 0 );
2547 SetProp( P_ALCOHOL, 0 );
2548 SetProp( P_BLIND, 0 );
2549 SetProp( P_DEAF, 0 );
2550 SetProp( P_POISON, 0 );
2551 SetProp( P_GHOST, 0 );
2552 SetProp( P_FROG, 0 );
2553 SetProp( P_HP, QueryProp(P_MAX_HP) );
2554 SetProp( P_SP, QueryProp(P_MAX_SP) );
2555 }
2556
2557 if ( QueryGuest() )
2558 Set( P_MAILADDR, "none" );
2559 else if ( !(mailaddr = Query(P_MAILADDR)) || mailaddr == "" ) {
2560 write(break_string(
2561 "Eine gueltige EMail-Adresse erleichtert es erheblich, Dir "
2562 "ein neues Passwort setzen zu lassen, falls Du einmal Dein "
2563 "Passwort vergisst.",78));
2564 input_to( "getmailaddr",INPUT_PROMPT,
2565 "Gib bitte Deine EMail-Adresse an: " );
2566 return;
2567 }
2568 InitPlayer2();
2569}
2570
2571/** liest eMail-Adresse vom Spieler ein und speichert sie.
2572 * Uebergibt anschliessend an InitPlayer2()
2573 * @param[in] maddr Spielereingabe der Emailadresse.
2574 * @sa InitPlayer2().
2575 */
2576static void getmailaddr( string maddr )
2577{
2578 maddr = check_email(maddr);
2579
2580 if ( !stringp(maddr)) {
2581 write("Deine Eingabe scheint keine gueltige EMail-Adresse gewesen "
2582 "zu sein.\n");
2583 input_to( "getmailaddr", INPUT_PROMPT,
2584 "Gib bitte Deine EMail-Adresse an: " );
2585 return;
2586 }
2587 Set( P_MAILADDR, maddr );
2588 InitPlayer2();
2589}
2590
2591
2592/** Prueft Geschlecht des Spielers und fragt ggf. beim Spieler nach.
2593 * Uebergibt an InitPlayer3() oder get_gender().
2594 * @sa InitPlayer3(), get_gender().
2595 */
2596private void InitPlayer2()
2597{
2598 if( member(({ MALE, FEMALE }), QueryProp(P_GENDER) ) == -1 ) {
2599 input_to( "getgender", INPUT_PROMPT,
2600 "Bist Du maennlich oder weiblich: ");
2601 return;
2602 }
2603
2604 InitPlayer3();
2605}
2606
2607/** Liest Spielerantwort auf die Frage nach dem Geschlecht des Chars ein.
2608 * Wird gerufen von input_to().
2609 * Uebergibt an InitPlayer3().
2610 * @sa InitPlayer3().
2611 */
2612static void getgender( string gender_string )
2613{
2614 gender_string = lower_case( gender_string );
2615
2616 if ( sizeof(gender_string)==1 && gender_string[0] == 'm' ){
2617 write( "Willkommen, mein Herr!\n" );
2618 SetProp( P_GENDER, MALE );
2619 }
2620 else if ( sizeof(gender_string)==1 && gender_string[0] == 'w' ){
2621 write( "Willkommen, gnae' Frau!\n" );
2622 SetProp( P_GENDER, FEMALE );
2623 }
2624 else {
2625 write( "Wie? Was? Verstehe ich nicht!\n" );
2626 input_to( "getgender", INPUT_PROMPT,
2627 "Bist Du maennlich oder weiblich? (tippe m oder w): ");
2628 return;
2629 }
2630
2631 InitPlayer3();
2632}
2633
2634
2635/** Prueft Terminaltyp des Spielers und fragt ggf. beim Spieler nach.
2636 * Uebergibt an InitPlayer4() oder gettty().
2637 * @sa InitPlayer4(), gettty().
2638 */
2639private void InitPlayer3()
2640{
2641 if ( !QueryProp(P_TTY) || QueryProp(P_TTY) == "none" )
2642 {
2643 write( "Waehle einen Terminaltyp (kann spaeter mit <stty> geaendert "
2644 "werden)\n");
2645 input_to( "gettty", INPUT_PROMPT, "vt100, ansi, dumb (Standard: dumb): " );
2646 return;
2647 }
2648 InitPlayer4();
2649}
2650
2651/** Liest Spielerantwort auf die Frage nach dem Terminaltyp ein.
2652 * Wird gerufen von input_to().
2653 * Uebergibt an InitPlayer4().
2654 * @sa InitPlayer4().
2655 */
2656static void gettty( string ttystr )
2657{
2658 if ( !ttystr || ttystr == "" )
2659 ttystr = "dumb";
2660
2661 ttystr = lower_case(ttystr);
2662
2663 if ( ttystr == "vt100" ){
2664 write( "Dies sollte " + ANSI_BOLD + "fett" + ANSI_NORMAL + " sein.\n" );
2665 SetProp( P_TTY, ttystr );
2666 }
2667 else
2668 if ( ttystr == "ansi" ){
2669 write( "Dies sollte " + ANSI_RED + "rot" + ANSI_NORMAL +
2670 " sein.\n" );
2671 SetProp( P_TTY, "ansi" );
2672 }
2673 else if ( ttystr == "dumb" ){
2674 write( "Ohje, oede! Besorg Dir ein besseres Terminal!\n" );
2675 SetProp( P_TTY, "dumb" );
2676 }
2677 else {
2678 write( "Dieser Terminaltyp wird nicht unterstuetzt. Nimm bitte "
2679 "einen aus:\nvt100, ansi or dumb (Standard ist dumb).\n" );
2680 input_to( "gettty", INPUT_PROMPT,
2681 "vt100, ansi or dumb (Standard ist dumb): ");
2682 return;
2683 }
2684
2685 InitPlayer4();
2686}
2687
2688
2689/** Liefert die UID des Charakters zurueck, also den Charakternamen.
2690 * @return UID des Objekts (Charaktername).
2691 */
2692nomask string query_real_name() {
2693 /* ACHTUNG !! DIES LFUN DARF NICHT ENTFERNT WERDEN !!! */
2694 /* Sie wird vom Gamedriver (zB bei F_ED) aufgerufen !! */
2695 /* Ich bin da zwar nicht so ueberzeugt von, dass der Driver die heutzutage
2696 * noch ruft, aber die halbe Mudlib ruft sie. ;-) (Zesstra, 27.4.08)
2697 */
2698 return getuid();
2699}
2700
2701/*
2702 * the wizard command review: show player moving messages
2703 */
2704int review() {
2705 string *msg;
2706 write(short());
2707 write("Deine Bewegungen werden wie folgt gemeldet:\n"+
2708 "mout: "+name(WER)+" "+(msg=explode(QueryProp(P_MSGOUT),"#"))[0]
2709 +" <Richtung>"+(sizeof(msg)>1 ? msg[1] : "")+".\n"+
2710 "min: "+name(WER)+" "+QueryProp(P_MSGIN)+".\n"+
2711 "mmout: "+name(WER)+" "+QueryProp(P_MMSGOUT)+".\n"+
2712 "mmin: "+name(WER)+" "+QueryProp(P_MMSGIN)+".\n"+
2713 (IS_LEARNER(ME) ?
2714 "cmsg: "+name(WER)+" "+QueryProp(P_CLONE_MSG)+".\n"+
2715 "dmsg: <Irgendetwas> "+QueryProp(P_DESTRUCT_MSG)+".\n"
2716 : "")+
2717 "Wenn Du jemanden angreifst, sieht das so aus:\n"+
2718 name(WER)+" greift Dich"+QueryProp(P_HANDS)[0]+" an.\n");
2719 return 1;
2720}
2721
2722/*
2723 * set player moving messages
2724 */
2725
2726static int setmin(string str)
2727{
2728 SetProp(P_MSGIN, _unparsed_args()||"kommt an");
2729 write("Ok.\n");
2730 return 1;
2731}
2732
2733static int setmout(string str)
2734{
2735 string *msg;
2736
2737 if(sizeof(msg=explode((_unparsed_args()||"geht"),"#"))>2)
2738 {
2739 write("Du darfst nur einmal '#' fuer die Richtung angeben.\n");
2740 return 1;
2741 }
2742 if(sizeof(msg)>1)
2743 {
2744 if (msg[0]!="" && msg[0][<1]==' ') msg[0]=msg[0][0..<2];
2745 SetProp(P_MSGOUT, msg[0]+"#"+msg[1]);
2746 }
2747 else
2748 SetProp(P_MSGOUT, _unparsed_args()||"geht");
2749 write("Ok.\n");
2750 return 1;
2751}
2752
2753static int setmmin(string str)
2754{
2755 SetProp(P_MMSGIN, _unparsed_args()||"erscheint");
2756 write("Ok.\n");
2757 return 1;
2758}
2759
2760static int setmmout(string str)
2761{
2762 SetProp(P_MMSGOUT, _unparsed_args()||"verschwindet");
2763 write("Ok.\n");
2764 return 1;
2765}
2766
2767static int setcmsg(string str)
2768{
2769 SetProp(P_CLONE_MSG, _unparsed_args()||"zaubert etwas aus "
2770 + QueryPossPronoun(MALE,WEM) + " Aermel hervor");
2771 write("Ok.\n");
2772 return 1;
2773}
2774
2775static int setdmsg(string str)
2776{
2777 SetProp(P_DESTRUCT_MSG, _unparsed_args()||"wird von " + name(WER,1)
2778 + " zerstaeubt");
2779 write("Ok.\n");
2780 return 1;
2781}
2782
2783static int set_title(string str)
2784{
2785 string bonus;
2786
2787 if(!IS_SEER(this_object()) && !FAO_HAS_TITLE_GIFT(this_object()) )
2788 {
2789 return 0;
2790 }
2791
2792 bonus = "";
2793 if (!(str=_unparsed_args()))
2794 str = "";
2795 else if( str[0..2]=="\\b," || str[0..2]=="\\b'" )
2796 {
2797 bonus = "\b"; // ein backspace fuer ein (hoch)komma ist ok! :-)
2798 str = str[2..];
2799 }
2800 if(str=="0") // damit der Gildentitel zum Vorschein kommen kann
2801 SetProp(P_TITLE, 0);
2802 else
2803 SetProp(P_TITLE, bonus+str);
2804 write("Ok.\n");
2805 return 1;
2806}
2807
2808static int extra_input(string str, string look)
2809{
2810 if (str=="**")
2811 {
2812 if (look=="")
2813 SetProp(P_EXTRA_LOOK,0);
2814 else
2815 SetProp(P_EXTRA_LOOK,look);
2816 write("Ok.\n");
2817 return 1;
2818 }
2819 input_to("extra_input",INPUT_PROMPT, "]" ,look+str+"\n");
2820 return 1;
2821}
2822
2823static int extralook(mixed str)
2824{
2825 if( str=="?" )
2826 {
2827 write( "Dein Extralook ist : "+QueryProp(P_EXTRA_LOOK) + "\n");
2828 return 1;
2829 }
2830 write("Bitte gib Deinen Extra-Look ein. Beenden mit **:\n");
2831 input_to("extra_input",INPUT_PROMPT,"]","");
2832 return 1;
2833}
2834
2835static void calculate_value()
2836{
2837 int i, carried_value, value;
2838
2839 carried_value=0;
2840 foreach(object ob: deep_inventory(ME)) {
2841 if (!ob->QueryProp(P_AUTOLOADOBJ))
2842 carried_value+=((value=(int)ob->QueryProp(P_VALUE)) > 1000 ? 1000 : value);
2843 }
2844 SetProp(P_CARRIED_VALUE, carried_value);
2845}
2846
2847/* Called by command 'save' */
2848int save_character() {
2849 save_me(1);
2850 write("Ok.\n");
2851 return 1;
2852}
2853
2854void save_me(mixed value_items)
2855{
2856 // Gaeste werden nicht gespeichert
2857 if( getuid()[0..3]=="gast" )
2858 return;
2859
2860 // Autoloader identifizieren und speichern
2861 autoload=([]);
2862 foreach(object ob: deep_inventory(ME)) {
2863 int val = ob->QueryProp( P_AUTOLOADOBJ );
2864 if (val && clonep(ob))
2865 {
2866 string obname=load_name(ob);
2867 if (obname == GELD)
2868 autoload[obname] += val;
2869 else
2870 autoload += ([obname:val]);
2871 }
2872 }
2873 // An noch nicht geclonte Autoloader denken!
2874 autoload += autoload_rest;
2875
2876 // Im Bedarfsfall Wert des Inventory bestimmen
2877 if (value_items)
2878 calculate_value();
2879 else
2880 SetProp(P_CARRIED_VALUE, 0);
2881
2882 // Logout-Zeit speichern
2883 if(query_once_interactive(ME) && !QueryProp(P_INVIS))
2884 Set(P_LAST_LOGOUT,time());
2885
2886 // Funktion zur Bestimmung des Gildenrating aufrufen
2887 string gilde=GUILD_DIR+QueryProp(P_GUILD);
2888 if (find_object(gilde) || file_size(gilde+".c")>-1)
2889 catch(call_other(gilde,"GuildRating",this_object());publish);
2890
2891 // Speichern des Spielers
Zesstra3162c892017-01-30 15:55:22 +01002892 save_object(SAVEPATH+getuid()[0..0]+"/" + getuid());
MG Mud User88f12472016-06-24 23:31:02 +02002893}
2894
2895static varargs void log_autoload( string file, string reason, mixed data, string error )
2896{
2897 if (member(autoload_error,file)!=-1) return;
2898 log_file(SHELLLOG("NO_AUTO_FILE"),sprintf("%s: %s: %s\nreason: cannot %s file\ndata: %O\n%s\n",
2899 ctime()[4..15],capitalize(getuid()),file,reason,data,
2900 (error?"Fehlermeldung: "+error+"\n":"")));
2901 autoload_error+=({file});
2902}
2903
2904// tics, die fuer autoloader reichen sollten:
2905#define SAFE_FOR_AUTOLOADER __MAX_EVAL_COST__/4
2906
2907private void load_auto_object( string file, mixed data )
2908{
2909 object ob;
2910 string error;
2911
2912 if( get_eval_cost() < SAFE_FOR_AUTOLOADER ) return;
2913 m_delete( autoload_rest, file );
2914 autoload_error-=({file});
2915
2916 if ( file == "/obj/money" )
2917 file = "/items/money";
2918 if ( file == "/obj/seercard" )
2919 file = "/items/seercard";
2920
2921 ob = find_object(file);
2922
2923 if (!ob)
2924 {
2925 if (file_size(file+".c")<0&&
2926 file_size(implode(explode(file,"/")[0..<2],"/")+
2927 "/virtual_compiler.c")<0)
2928 {
2929 log_autoload(file,"find",data,0);
2930 return;
2931 }
2932 if (error = catch(load_object(file); publish))
2933 {
2934 log_autoload(file,"load",data,error);
2935 return;
2936 }
2937 }
2938 if ( error = catch(ob = clone_object(file); publish) )
2939 {
2940 log_autoload(file,"clone",data, error);
2941 return;
2942 }
2943
2944 if ( error = catch(ob->SetProp( P_AUTOLOADOBJ, data ); publish) )
2945 {
2946 log_autoload(file,"SetProp",data, error);
2947 ob->remove(1);
2948 if (ob) destruct(ob);
2949 return;
2950 }
2951
2952 if ( error = catch(ob->move( ME, M_NOCHECK );publish) ) {
2953 log_autoload(file,"move",data, error);
2954 ob->remove(1);
2955 if(ob) destruct(ob);
2956 return;
2957 }
2958}
2959
2960static void load_auto_objects( mapping map_ldfied )
2961{
2962 if ( (!mappingp(map_ldfied) || !sizeof(map_ldfied)) && !sizeof(autoload_rest) )
2963 return;
2964
2965 // Mit Netztoten Spielern rechnen manche Autoloader nicht. Also
2966 // das Clonen unterbrechen und in Reconnect() wieder anwerfen.
2967 if ( !interactive() )
2968 return;
2969
2970 // Kleiner Hack: autoload_rest ist eine globale Variable, die beim
2971 // Clonen der einzelnen Autoloader direkt veraendert wird.
2972 // So lange das Mapping noch Eintraege hat, muessen wir noch fehlende
2973 // Autoloader clonen.
2974 if ( !sizeof(autoload_rest) )
2975 autoload_rest = map_ldfied;
2976
2977 // Schon hier einen call_out() zum "Nach"clonen von noch nicht geclonten
2978 // Autoloadern starten, da spaeter u.U. keine Rechenzeit mehr dafuer da ist.
2979 while ( remove_call_out("load_auto_objects") != -1 )
2980 /* do nothing */;
2981
2982 // Mit Parameter '0' aufrufen, da das globale Mapping benutzt wird.
2983 // Verzoegerung 0 in rekursiven Callouts ist bloed, also 1s Delay
2984 call_out( "load_auto_objects", 2, 0 );
2985
2986 // Mit catch() gegen die Evalcost-Falle!
2987 // Mit Absicht das walk_mapping() aus der "alten" Version erhalten und
2988 // nicht durch eine (einfachere) Schleife inkl. get_eval_cost() ersetzt,
2989 // da eine Schleife gegenueber der walk_mapping()-Loesung den Aufbau
2990 // der previous_object()-Kette veraendern wuerde; darauf testen aber
2991 // manche Objekte.
2992 catch( walk_mapping( autoload_rest, #'load_auto_object/*'*/ ) );
2993}
2994
2995/*
2996 * al_to_title: Make the numeric alignment value into a string
2997 */
2998static string al_to_title(int a)
2999{
3000 if (a >= KILL_NEUTRAL_ALIGNMENT * 100)
3001 return "heilig";
3002 if (a > KILL_NEUTRAL_ALIGNMENT * 20)
3003 return "gut";
3004 if (a > KILL_NEUTRAL_ALIGNMENT * 4)
3005 return "nett";
3006 if (a > - KILL_NEUTRAL_ALIGNMENT * 4)
3007 return "neutral";
3008 if (a > - KILL_NEUTRAL_ALIGNMENT * 20)
3009 return "frech";
3010 if (a > - KILL_NEUTRAL_ALIGNMENT * 100)
3011 return "boese";
3012 return "satanisch";
3013}
3014
3015static int toggle_whimpy_dir(string str) {
3016 SetProp(P_WIMPY_DIRECTION,str=_unparsed_args()||str);
3017 if (str)
3018 printf("Ok, Fluchtrichtung %O.\n",str);
3019 else
3020 printf("Ok, bevorzugte Fluchtrichtung deaktiviert.\n");
3021 return 1;
3022}
3023
3024static int toggle_whimpy(string str)
3025{
3026 int i;
3027
3028 if(!str || str=="" || (sscanf(str,"%d",i)<0))
3029 {
3030 write("vorsicht <hp>, 0<=hp<"+QueryProp(P_MAX_HP)+"\n");
3031 return 1;
3032 }
3033 if(i>=QueryProp(P_MAX_HP) || i<0)
3034 {
3035 write("Der Wert ist nicht erlaubt.\n");
3036 return 1;
3037 }
3038 if(!i) write("Prinz Eisenherz-Modus.\n");
3039 else write("Vorsicht-Modus ("+i+")\n");
3040 SetProp(P_WIMPY,i);
3041 return 1;
3042}
3043
3044/** Bestimmt, ob das Spielerobjekt beschattet werden darf.
3045 * Beschatten ist nur fuer Objekte erlaubt, die in /std/player/shadows
3046 * abgelegt sind.
3047 * Ausnahme: Testspieler mit gesetztem P_ALLOWED_SHADOW
3048 * @param[in] obj Objekt, was beschatten moechte.
3049 * @return 0, wenn Beschatten erlaubt, 1 sonst.
3050 */
3051varargs nomask int query_prevent_shadow(object obj)
3052{
3053 string what, allowed_shadow;
3054 int dummy;
3055
3056// if ( Query(P_TESTPLAYER) )
3057// return 0;
3058
3059 if (obj){
3060 what=object_name(obj);
3061 if (what && what != "" &&
3062 sscanf(what,"/std/player/shadows/%s#%d",what,dummy)==2)
3063 return 0;
3064
3065 // Einem Testspieler kann man P_ALLOWED_SHADOW auf einen zu testenden
3066 // Shadow setzen.
3067 if ( Query(P_TESTPLAYER) &&
3068 stringp(what) &&
3069 stringp(allowed_shadow=Query(P_ALLOWED_SHADOW)) &&
3070 strstr(what, allowed_shadow)==0)
3071 return 0;
3072 }
3073 return 1;
3074}
3075
3076static int uhrzeit()
3077{
3078 write(dtime(time()+QueryProp(P_TIMEZONE)*3600)+".\n");
3079 return 1;
3080}
3081
3082protected string SetDefaultHome(string str)
3083{
3084 return default_home=str;
3085}
3086
3087string QueryDefaultHome()
3088{
3089 return default_home;
3090}
3091
3092protected string SetDefaultPrayRoom(string str)
3093{
3094 if(hc_play>1)
3095 {
3096 default_pray_room="/room/nirvana";
3097 }
3098 else
3099 default_pray_room=str;
3100
3101 return default_pray_room;
3102}
3103
3104string QueryPrayRoom()
3105{
3106 if(hc_play>1)
3107 {
3108 return "/room/nirvana";
3109 }
3110 string room = QueryProp(P_PRAY_ROOM);
3111 if (stringp(room))
3112 return room;
3113 else if (default_pray_room)
3114 return default_pray_room;
3115 // hoffentlich ist das wenigstens gesetzt.
3116 return default_home;
3117}
3118
3119void _restart_beat()
3120{
3121 tell_object(ME,
3122 "Der GameDriver teilt Dir mit: Dein Herzschlag hat wieder eingesetzt.\n");
3123 set_heart_beat(1);
3124}
3125
3126static int weg(string str)
3127{
3128 if (!(str=_unparsed_args()))
3129 {
3130 printf("Du bist nicht%s als abwesend gekennzeichnet.\n",
3131 QueryProp(P_AWAY) ? " mehr" : "");
3132 SetProp(P_AWAY, 0);
3133 return 1;
3134 }
3135 write("Du bist jetzt als abwesend gekennzeichnet.\n");
3136 SetProp(P_AWAY, str);
3137 return 1;
3138}
3139
3140/* Ein Befehl zum anschauen der Wegmeldung anderer Spieler */
3141static int wegmeldung(string player)
3142{
3143
3144 object player_ob;
3145 string weg;
3146
3147 if ( !player || player=="" ||
3148 player==lowerstring(this_player()->QueryProp(P_NAME)))
3149 {
3150 weg=this_player()->QueryProp(P_AWAY);
3151 write ("Du bist "+(weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
3152 if (weg)
3153 write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
3154 return 1;
3155 }
3156
3157 // Welcher Spieler ist gemeint?
3158 player_ob=find_player(player);
3159
3160 // Spieler nicht da oder Invis und Anfrager is kein Magier
3161 if (!player_ob ||
3162 (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
3163 {
3164 write(capitalize(player)+" ist gerade nicht im Spiel.\n");
3165 return 1;
3166 }
3167
3168 weg=player_ob->QueryProp(P_AWAY);
3169
3170 // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
3171 write (player_ob->QueryProp(P_NAME)+" ist "+
3172 (weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
3173
3174 if (weg)
3175 write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
3176
3177 return 1;
3178}
3179
3180static string timediff(int time)
3181{
3182 string ret;
3183
3184 ret="";
3185 if(time>=86400) {
3186 ret+=time/86400+"d ";
3187 time%=86400;
3188 }
3189 if(time<36000) ret+="0";
3190 ret+=time/3600+":";
3191 time%=3600;
3192 if(time<600) ret+="0";
3193 ret+=time/60+":";
3194 time%=60;
3195 if(time<10) ret+="0";
3196 ret+=time+"";
3197 return ret;
3198}
3199
3200
3201/* Ein Befehl zum anschauen der Idlezeit anderer Spieler */
3202static int idlezeit(string player)
3203{
3204
3205 object player_ob;
3206 int idle;
3207
3208 if ( !player || player=="" ||
3209 player==lowerstring(this_player()->QueryProp(P_NAME)))
3210 {
3211 write ("Du bist selber natuerlich gerade nicht idle.\n");
3212 return 1;
3213 }
3214
3215 // Welcher Spieler ist gemeint?
3216 player_ob=find_player(player);
3217
3218 // Spieler nicht da oder Invis und Anfrager is kein Magier
3219 if (!player_ob ||
3220 (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
3221 {
3222 write(capitalize(player)+" ist gerade nicht im Spiel.\n");
3223 return 1;
3224 }
3225
3226 idle=query_idle(player_ob);
3227
3228 // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
3229 write (player_ob->QueryProp(P_NAME)+" ist "+
3230 (idle>=60?timediff(idle):"nicht")+" passiv.\n");
3231
3232 return 1;
3233}
3234
3235
3236/** Belebt einen netztoten Spieler wieder.
3237 * Reaktiviert Heartbeats, bewegt den Spieler zurueck an den Ort, der eim
3238 * Einschlafen zum Aufwachen ermittelt wurde (im einfachsten Fall der Raum,
3239 * wo man eingeschlafen ist).
3240 */
3241static void ndead_revive()
3242{
3243 string fname;
3244 int ret;
3245
3246 set_heart_beat(1);
3247 ndead_next_check = NETDEAD_CHECK_TIME;
3248 ndead_currently = 0;
3249 ndead_lasttime = 0;
3250
3251 if ( !objectp(ndead_location) &&
3252 stringp(ndead_l_filename) && sizeof(ndead_l_filename)) {
3253
3254 if ( member( ndead_l_filename, '#' ) == -1 ){
3255 catch(load_object( ndead_l_filename); publish);
3256 ndead_location = find_object(ndead_l_filename);
3257 }
3258 else {
3259 ndead_location = find_object(ndead_l_filename);
3260 if ( !ndead_location && env_ndead_info ){
3261 fname = explode( ndead_l_filename, "#" )[0];
3262 catch(ndead_location =
3263 (load_object(fname)->SetProp(P_NETDEAD_INFO, env_ndead_info));
3264 publish);
3265 if ( !objectp(ndead_location) ){
3266 catch(load_object( ndead_location);publish);
3267 ndead_location = find_object(ndead_location);
3268 }
3269 }
3270 }
3271 }
3272
3273 if ( !objectp(ndead_location)
3274 || catch(ret = move( ndead_location, M_GO|M_SILENT );publish)
3275 || ret != 1 ) {
3276 move( "gilden/abenteurer", M_GO|M_SILENT );
3277 ndead_location = environment();
3278 }
3279
3280 // ndead_location=0;
3281 ndead_l_filename = 0;
3282 env_ndead_info = 0;
3283}
3284
3285/** Bewegt einen netztoten Spieler in den Netztotenraum
3286 * Gerufen von heartbeat().
3287 * Zerstoert Gaeste, verlaesst ggf. das Team, ermittelt, ob der Spieler beim
3288 * Aufwachen in das alte Environment bewegt werden soll oder in einen anderen
3289 * Raum, hierzu wird im Environment P_NETDEAD_INFO abgefragt.
3290 * Deaktiviert die Kommandos per disable_commands().
3291 */
3292static void ndead_move_me() {
3293 object team;
3294 mixed amem;
3295
3296 set_heart_beat(0);
3297 stop_heart_beats();
3298 if (QueryGuest()) {
3299 quit();
3300 if (ME)
3301 remove();
3302 if (ME)
3303 destruct(ME);
3304 return;
3305 }
3306 ndead_next_check=NETDEAD_CHECK_TIME;
3307 ndead_currently=1;
3308 ndead_lasttime=0;
3309 ndead_location=environment();
3310 if (objectp(ndead_location))
3311 ndead_l_filename=object_name(ndead_location);
3312
3313 if (objectp(environment())
3314 && sizeof(explode(object_name(environment()),"#")) > 1)
3315 env_ndead_info=environment()->QueryProp(P_NETDEAD_INFO);
3316 else
3317 env_ndead_info=0;
3318
3319 if ( objectp(team=Query(P_TEAM)) )
3320 // Der Test auf assoziierte Teammitglieder (== FolgeNPCs)
3321 // verhindert, dass Spieler nach "schlafe ein" aus dem
3322 // Team ausgetragen werden. -- 29.01.2002 Tiamak
3323 // && !objectp(amem=Query(P_TEAM_ASSOC_MEMBERS))
3324 // && !(pointerp(amem) && sizeof(amem)))
3325 team->RemoveMember(ME);
3326
3327 disable_commands();
3328 move(NETDEAD_ROOM,M_GO|M_NO_ATTACK|M_NOCHECK,"ins Reich der Netztoten");
3329}
3330
3331/** Ist dieser Character ein Gast?
3332 * @return 1, wenn Gast, 0 sonst.
3333 */
3334int QueryGuest()
3335{
3336 string dummy;
3337 return sscanf(getuid(),"gast%d",dummy);
3338}
3339
3340/** Spielerkommando 'schlafe ein'.
3341 * Ruft remove_interactive() bei Spielern, bei Magiern wird quit() gerufen,
3342 * um das Magierobjekt zu zerstoeren.
3343 * @sa quit()
3344 */
3345int disconnect(string str)
3346{
3347 string verb;
3348 string desc = break_string(
3349 "\"schlafe ein\" beendet Deine Verbindung mit "MUDNAME". Du behaeltst "
3350 "Deine Sachen.\nFalls "MUDNAME" jedoch abstuerzt oder neu gestartet "
3351 "wird, waehrend Du weg bist, verlierst Du die meisten allerdings "
3352 "(genauso, als wenn Du Deine Verbindung mit \"ende\" beendet haettest). "
3353 "In diesem Fall bekommst Du dann eine kleine Entschaedigung."
3354 ,78,0,BS_LEAVE_MY_LFS);
3355
3356 verb=query_verb();
3357 if (!verb)
3358 verb="AUTOCALL";
3359 if (verb[0..5]=="schlaf" && str!="ein")
3360 {
3361 notify_fail(desc);
3362 return 0;
3363 }
3364 if (IS_LEARNER(this_object()))
3365 return quit();
3366
3367 tell_object(this_object(), desc);
3368
3369 if (clonep(environment()) && !environment()->QueryProp(P_NETDEAD_INFO))
3370 tell_object(this_object(),break_string(
3371 "\nACHTUNG: Wenn Du hier laengere Zeit schlaefst, "
3372 "kommst Du vermutlich nicht an diesen Ort zurueck!",78));
3373
3374 say(capitalize(name(WER))+" hat gerade die Verbindung zu "MUDNAME" gekappt.\n");
3375 remove_interactive(ME);
3376 call_out(#'clear_tell_history,4);
3377 return 1;
3378}
3379
3380static int finger (string str)
3381{
3382 string ret;
3383 mixed xname;
3384
3385 if (!str || str==""
3386 || sizeof(explode(str," ")-({"-n","-p","-s","-v","-a"}))>1)
3387 {
3388 write("finger <spielername> oder finger <spielername@mudname>\n"+
3389 "Bitte nur den reinen Spielernamen verwenden, keine Namensvorsaetze oder Titel\n");
3390 return 1;
3391 }
3392 xname=explode(str,"@");
3393 if(sizeof(xname)==2)
3394 {
3395 if (xname[0]=="-n " || xname[0]=="-p " || xname[0]=="-s ") {
3396 write("finger <spielername>@<mudname> - der Spielername fehlt.\n");
3397 return 1;
3398 }
3399 if (ret=INETD->_send_udp(xname[1],([
3400 REQUEST: "finger",
3401 SENDER: getuid(ME),
3402 DATA: (explode(xname[0]," ")-({"-n","-p","-s"}))[0]
3403 ]), 1))
3404 write(ret);
3405 else
3406 write("Anfrage abgeschickt.\n");
3407 return 1;
3408 }
3409 "/p/daemon/finger"->finger_single(str,1);
3410 return 1;
3411}
3412
3413string lalign(string str, int wid)
3414{
3415 return (str+" "+
3416 " ")[0..wid-1];
3417}
3418
3419#define MUDS_BAR "\
3420-------------------------------------------------------------------------------"
3421
3422private void format(mixed mud, mixed hosts, string output)
3423{
3424 output += lalign(hosts[mud][HOST_NAME], 20) + " " +
3425 (hosts[mud][HOST_STATUS] ?
3426 hosts[mud][HOST_STATUS] > 0 ?
3427 "UP " + ctime(hosts[mud][HOST_STATUS])[4..15] :
3428 "DOWN " + ctime(-hosts[mud][HOST_STATUS])[4..15]
3429 : "UNKNOWN Never accessed.") + "\n";
3430}
3431
3432static int muds() {
3433 mapping hosts;
3434 int i;
3435 mixed muds, output;
3436
3437 output = lalign("Mudname", 20) + " Status Last access";
3438 output += "\n" + MUDS_BAR[0..sizeof(output)] + "\n";
3439 muds = sort_array(m_indices(hosts = INETD->query("hosts")),#'>);
3440 map(muds, #'format, hosts, &output);
3441 More(output);
3442 return 1;
3443}
3444
3445// **** local property methods
3446static int _set_level(int i)
3447{
3448 if (!intp(i)) return -1;
3449 if (i<1) return -1;
3450 Set(P_LEVEL, i);
3451 GMCP_Char( ([P_LEVEL: i]) );
3452 return i;
3453}
3454
3455static int _set_invis(int a)
3456{
3457 return Set(P_INVIS, intp(a) ? a : !Query(P_INVIS));
3458}
3459
3460/* sets the terminal type */
3461/* note: support vt100 (b/w), ansi (color), dumb (none) */
3462static string _set_tty(string str) {
3463 if(str != "dumb" && str != "vt100" && str != "ansi")
3464 return Query(P_TTY);
3465 return Set(P_TTY, str);
3466}
3467
3468static int stty(string str)
3469{
3470 if(str!="dumb"&&str!="vt100"&&str!="ansi"&&str!="reset")
3471 {
3472 write("Kommando: stty dumb|vt100|ansi oder reset\n");
3473 }
3474 if(str == "reset") {
3475 printf("Dieser Text sollte lesbar sein!\n");
3476 return 1;
3477 }
3478
3479 write("TTY steht jetzt auf "+SetProp(P_TTY,str)+".\n");
3480 if(str == "ansi" || str == "vt100") {
3481 printf("Terminal Test:\n");
3482 printf("VT100: fett unterstrichen "+
3483 "blinkend invers\n");
3484 if(str == "ansi") {
3485 printf("ANSI Farben und VT100 Attribute:\n");
3486 foreach(int fg: 30 .. 37) {
3487 foreach(int bg: 40 .. 47) {
3488 printf("[%d;%dm@", fg, bg);
3489 printf("[%d;%dm@", fg, bg);
3490 printf("[%d;%dm@", fg, bg);
3491 printf("[%d;%dm@", fg, bg);
3492 }
3493 printf("\n");
3494 }
3495 printf("Sollte dieser Text hier nicht richtig lesbar\nsein,"+
3496 "benutze das Kommando stty reset!\n");
3497 }
3498
3499 }
3500 return 1;
3501}
3502
3503int set_ascii_art(string str)
3504{
3505 if (str!="ein"&&str!="aus")
3506 {
3507 printf("Du moechtest 'Grafik' "+(QueryProp(P_NO_ASCII_ART)?"NICHT ":"")+
3508 "sehen.\n");
3509 }
3510
3511 if (str=="ein") {
3512 SetProp(P_NO_ASCII_ART, 0);
3513 printf("Zukuenftig moechtest Du 'Grafik' sehen.\n");
3514 }
3515
3516 if (str=="aus") {
3517 SetProp(P_NO_ASCII_ART, 1);
3518 printf("Zukuenftig moechtest Du KEINE 'Grafik' mehr sehen.\n");
3519 }
3520
3521
3522 return 1;
3523}
3524
3525int _set_shell_version(int arg)
3526{
3527 if (!intp(arg))
3528 return -1;
3529 Set(P_SHELL_VERSION,({QueryProp(P_RACE),arg}));
3530 return 1;
3531}
3532
3533int _query_shell_version()
3534{ mixed sv;
3535
3536 if (!(sv=Query(P_SHELL_VERSION)) || !pointerp(sv) || sizeof(sv)!=2 ||
3537 sv[0]!=QueryProp(P_RACE) || !intp(sv[1]))
3538 return 0;
3539 return sv[1];
3540}
3541
3542// XxXxXxXxXx
3543
3544int more(string str)
3545{
3546 if(!str)
3547 {
3548 notify_fail("Usage: more <file>\n");
3549 return 0;
3550 }
3551 if (file_size(str) <= 0) {
3552 notify_fail(str+": No such file\n");
3553 return 0;
3554 }
3555 More(str, 1);
3556 return 1;
3557}
3558
3559static int set_visualbell(string str)
3560{
3561 if(!str)
3562 {
3563 write("Derzeitige Einstellung fuer Tonausgabe: "+
3564 (QueryProp(P_VISUALBELL)?"AUS":"EIN")+".\n");
3565 return 1;
3566 }
3567 if (str=="ein")
3568 {
3569 if(!QueryProp(P_VISUALBELL))
3570 write("Die Tonausgabe stand schon auf EIN.\n");
3571 else
3572 {
3573 SetProp(P_VISUALBELL,0);
3574 write("OK, Tonausgabe auf EIN gestellt.\n");
3575 }
3576 }
3577 else
3578 if (str=="aus")
3579 {
3580 if(QueryProp(P_VISUALBELL))
3581 write("Die Tonausgabe stand schon auf AUS.\n");
3582 else
3583 {
3584 SetProp(P_VISUALBELL,1);
3585 write("OK, Tonausgabe auf AUS gestellt.\n");
3586 }
3587 }
3588 else
3589 write("Syntax: ton [ein|aus]\n");
3590 return 1;
3591}
3592
3593static int set_screensize(string str)
3594{
3595 int size;
3596
3597 if (str && (str[0..2] == "abs" || str[0..2]=="rel")) {
3598 size = QueryProp(P_MORE_FLAGS);
3599 if (str[0..2] == "abs") {
3600 size |= E_ABS;
3601 write("Es wird beim Prompt die Zeilenzahl des Textes angegeben.\n");
3602 }
3603 else {
3604 size &= ~E_ABS;
3605 write("Es wird beim Prompt der prozentuale Anteil des Textes angegeben.\n");
3606 }
3607 SetProp(P_MORE_FLAGS, size);
3608 return 1;
3609 }
3610
3611 if ( str && (str=="auto" || sscanf( str, "auto %d", size )) ){
3612 if ( size > 0 ){
3613 write("Ungueltiger Wert! "
3614 "In Verbindung mit 'auto' sind nur negative Werte erlaubt.\n");
3615 return 1;
3616 }
3617
3618 SetProp( P_SCREENSIZE, size-1 );
3619
3620 write("Ok, Deine Zeilenzahl wird nun automatisch ermittelt (derzeit "+
3621 QueryProp(P_SCREENSIZE)+").\n"+
3622 break_string("Bitte beachte, dass dies nur einwandfrei "
3623 "funktioniert, wenn Dein Client Telnetnegotiations "
3624 "unterstuetzt (siehe auch \"hilfe telnegs\").") );
3625 return 1;
3626 }
3627
3628 if ( !str || str=="" || !sscanf( str, "%d", size ) || size < 0 || size > 100){
3629 write(break_string(
3630 sprintf("Mit dem Befehl 'zeilen <groesse>' kannst Du einstellen, "
3631 "wieviele Zeilen bei mehrseitigen Texten auf einmal ausgegeben "
3632 "werden. Die angegebene Groesse muss zwischen 0 und 100 liegen. "
3633 "Bei Groesse 0 wird einfach alles ausgegeben (ohne Pause). Mit "
3634 "der Einstellung 'auto' wird die Groesse automatisch ueber "
3635 "die Telnetnegotiations ermittelt (siehe auch 'hilfe telnegs'). "
3636 "Um nach einer Seite Text noch etwas Platz zu haben, kann man z.B. "
3637 "'zeilen auto -3' einstellen.\n"
3638 "Die Voreinstellung ist 20 Zeilen.\n"
3639 "Mit 'zeilen abs[olut]' und 'zeilen rel[ativ]' kannst Du fest"
3640 "legen, ob im Prompt bei langen Texten die aktuelle Zeilennummer "
3641 "oder eine prozentuale Angabe ausgegeben wird.\n"
3642 "Deine aktuelle Einstellung ist %d%s Zeilen (%s).",
3643 QueryProp(P_SCREENSIZE),
3644 Query(P_SCREENSIZE) < 0 ? " 'automatische'" : "",
3645 QueryProp(P_MORE_FLAGS) & E_ABS ? "absolut" : "relativ"),78,0,1));
3646 return 1;
3647 }
3648
3649 SetProp( P_SCREENSIZE, size );
3650
3651 printf( "Okay, Deine Zeilenzahl steht nun auf %d.\n", size );
3652 return 1;
3653}
3654
3655static int _query_screensize()
3656{
3657 int sz,rows;
3658
3659 if ( (sz=Query(P_SCREENSIZE)) >= 0 )
3660 return sz;
3661
3662 if ( !rows=QueryProp(P_TTY_ROWS) )
3663 return 0;
3664
3665 return (rows+=sz) >= 5 ? rows : 5;
3666}
3667
3668static int presay(string str)
3669{
3670 if (!str=_unparsed_args())
3671 write("Dein Presay ist jetzt geloescht.\n");
3672 else
3673 printf("Dein Presay lautet jetzt: \"%s\".\n",str=capitalize(str));
3674 SetProp(P_PRESAY,str);
3675 return 1;
3676}
3677
3678static int sethands(string str)
3679{
3680 mixed *hands;
3681
3682 if (!(str=_unparsed_args()))
3683 {
3684 write("sethands <message>\n");
3685 return 1;
3686 }
3687 if (str=="0")
3688 hands=RaceDefault(P_HANDS);
3689 if (!hands || !pointerp(hands))
3690 hands=Query(P_HANDS);
3691 hands[0]=" "+str;
3692 Set(P_HANDS,hands);
3693 write("Ok.\n");
3694 return 1;
3695}
3696
3697static int inform(string str)
3698{
3699 switch (str) {
3700 case "on":
3701 case "ein":
3702 case "an":
3703 if (Query(P_INFORMME))
3704 write("Das hattest Du schon so eingestellt.\n");
3705 else
3706 {
3707 write("Kuenftig wirst Du informiert, wenn jemand das "MUDNAME" verlaesst/betritt.\n");
3708 Set(P_INFORMME,1);
3709 }
3710 return 1;
3711 case "aus":
3712 case "off":
3713 if (!Query(P_INFORMME))
3714 write("Das hattest Du schon so eingestellt.\n");
3715 else
3716 {
3717 write("Ok.\n");
3718 Set(P_INFORMME,0);
3719 }
3720 return 1;
3721 case 0:
3722 write("Inform-Mode ist "+(Query(P_INFORMME)?"an":"aus")+"geschaltet.\n");
3723 return 1;
3724 }
3725 write("inform an oder inform aus, bitte.\n");
3726 return 1;
3727}
3728
3729void delayed_write(mixed *what)
3730{
3731 if (!pointerp(what)||!sizeof(what)||!pointerp(what[0]))
3732 return;
3733 tell_object(ME,what[0][0]);
3734 if (sizeof(what)>1&&sizeof(what[0])>1)
3735 call_out("delayed_write",what[0][1],what[1..]);
3736}
3737
3738void notify_player_change(string who, int rein, int invis)
3739{
3740 string *list,name;
3741 mixed mlist;
3742
3743 if (invis) name="("+who+")";
3744 else name=who;
3745
3746 if (Query(P_INFORMME))
3747 {
3748 if (rein)
3749 tell_object(ME,name+" ist gerade ins "MUDNAME" gekommen.\n");
3750 else
3751 tell_object(ME,name+" hat gerade das "MUDNAME" verlassen.\n");
3752 }
3753
3754 if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
3755
3756 if(pointerp(list=Query(P_WAITFOR)) && sizeof(list) && member(list,who)!=-1)
3757 {
3758 if (!QueryProp(P_VISUALBELL))
3759 name+=sprintf("%c",7); // Char fuer Pieps an den String anhaengen.
3760 // Moechte der Spieler keine ASCII-Grafik sehen, wird diese Meldung ohne
3761 // Leerzeichen formatiert, so dass sie von Screenreadern vorgelesen wird.
3762 // Anderenfalls wuerde sie einzeln buchstabiert.
3763 if ( QueryProp(P_NO_ASCII_ART) )
3764 {
3765 delayed_write( ({ ({ sprintf("%s IST JETZT %sDA !!!\n",
3766 name, (rein?"":"NICHT MEHR ")) }) }) );
3767 }
3768 else
3769 {
3770 delayed_write( ({ ({ sprintf("%s I S T J E T Z T %sD A !!!\n",
3771 name, (rein?"":"N I C H T M E H R ")) }) }) );
3772 }
3773 }
3774
3775 if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
3776 (mappingp(mlist)) && (mlist[who]))
3777 Show_WaitFor_Reason(who,invis);
3778}
3779
3780static int erwarte(string str)
3781{
3782 string *list,*str1;
3783 mixed mlist;
3784 int i;
3785
3786 if (!mappingp(mlist=QueryProp(P_WAITFOR_REASON)))
3787 mlist=([]);
3788 if (!pointerp(list=Query(P_WAITFOR)))
3789 list=({});
3790
3791 if (!str || str=="-u")
3792 {
3793 if(Query(P_WAITFOR_FLAGS)&0x01)
3794 write("Du hast 'erwarte' temporaer deaktiviert.\n");
3795 write("Du erwartest jetzt");
3796 if (!sizeof(list))
3797 write(" niemanden mehr.\n");
3798 else
3799 {
3800 write(":\n");
3801 if (!str) list=sort_array(list,#'>);
3802 More(break_string(CountUp(list),78));
3803 }
3804 return 1;
3805 }
3806 if(str=="aus"){
3807 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)|0x01);
3808 write("Erwarte ist jetzt deaktiviert.\n");
3809 return 1;
3810 }
3811 if(str=="an" || str=="ein"){
3812 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)&0xFE);
3813 write("Erwarte ist jetzt aktiv.\n");
3814 return 1;
3815 }
3816
3817 str1=explode(_unparsed_args()||""," ");
3818 if (sizeof(str1)==1)
3819 {
3820 if (str1[0]!="wegen")
3821 {
3822 str=capitalize(lower_case(str));
3823 if (member(list,str)!=-1)
3824 {
3825 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,str));
3826 list-=({str});
3827 write(str+" aus der Liste entfernt.\n");
3828 } else
3829 {
3830 if (sizeof(list)>1000)
3831 {
3832 write("Du erwartest schon genuegend!\n");
3833 return 1;
3834 }
3835 list+=({str});
3836 write(str+" an die Liste angehaengt.\n");
3837 }
3838 Set(P_WAITFOR,list);
3839 }
3840 else
3841 {
3842 if (sizeof(mlist) && sizeof(list=m_indices(mlist)))
3843 {
3844 write("Du erwartest aus einem bestimmten Grund:\n");
3845 write(break_string(CountUp(sort_array(list,#'>))+".",78));
3846 }
3847 else write("Du erwartest niemanden aus einem bestimmten Grund.\n");
3848 }
3849 return 1;
3850 }
3851 notify_fail("Falsche Syntax, siehe 'hilfe erwarte'!\n");
3852 if (str1[1]!="wegen") return 0;
3853 if (sizeof(str1)==2)
3854 Show_WaitFor_Reason(capitalize(lower_case(str1[0])),0);
3855 else {
3856 string s=capitalize(lower_case(str1[0]));
3857 if (sizeof(str1)==3 && (str1[2]=="nichts" || str1[2]=="loeschen"))
3858 if (!mlist[s])
3859 write("Du hast "+s+" aus keinem bestimmten Grund erwartet!\n");
3860 else
3861 {
3862 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,s));
3863 write("Du erwartest "+s+" aus keinem bestimmten Grund mehr!\n");
3864 }
3865 else
3866 {
3867 if (IS_ARCH(ME)) i=80; else if (IS_LEARNER(ME)) i=40;
3868 else if (IS_SEER(ME)) i=20; else i=10;
3869 if (!mlist[s] && sizeof(mlist)>=i)
3870 write("Sorry, aber Du erwartest schon genuegend Leute!\n");
3871 else
3872 {
3873 SetProp(P_WAITFOR_REASON,mlist+([s:implode(str1[2..]," ")]));
3874 Show_WaitFor_Reason(s,0);
3875 }
3876 }
3877 }
3878 return 1;
3879}
3880
3881static int uhrmeldung(string str)
3882{
3883 if (!(str=_unparsed_args()))
3884 {
3885 str=QueryProp(P_CLOCKMSG);
3886 if (!str)
3887 {
3888 write("Du hast die Standard-Uhrmeldung.\n");
3889 return 1;
3890 }
3891 if( !stringp(str) ) str = sprintf("%O\n",str);
3892 printf("Deine Uhrmeldung ist:\n%s\n",str[0..<2]);
3893 return 1;
3894 }
3895 if (str=="0")
3896 {
3897 SetProp(P_CLOCKMSG,0);
3898 write("Ok, Du hast jetzt wieder die Standard-Meldung.\n");
3899 return 1;
3900 }
3901 if (sizeof(explode(str,"%d"))>2)
3902 {
3903 write("Fehler, es darf nur ein %d in der Meldung vorkommen.\n");
3904 return 1;
3905 }
3906 /* Mehrere %-Parameter verursachen das Abschalten der Uhr zur vollen Stunde.
3907 */
3908 if (sizeof(explode(str,"%"))>2)
3909 {
3910 write("Fehler: Zuviele %-Parameter in der Meldung.\n");
3911 return 1;
3912 }
3913 /* Nur ein %-Parameter, aber der falsche: nicht sinnvoll. */
3914 else
3915 {
3916 int i = strstr(str,"%",0);
3917 if ( i>-1 && ( i==sizeof(str)-1 || str[i+1]!='d'))
3918 {
3919 write("Fehler: Falscher %-Parameter in der Meldung.\n");
3920 return 1;
3921 }
3922 }
3923 str+="\n";
3924 SetProp(P_CLOCKMSG,str);
3925 write("Ok.\n");
3926 return 1;
3927}
3928
3929static int zeitzone(string str)
3930{
3931 int zt;
3932 if(!str || str==""){
3933 if(!(zt=QueryProp(P_TIMEZONE)))
3934 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
3935 "eingestellt.\n");
3936 else if(zt>0)
3937 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
3938 "eingestellt.\n",zt);
3939 else
3940 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
3941 "Berlin eingestellt.\n",-zt);
3942 return 1;
3943 }
3944 if(sscanf(str,"utc %d",zt)==1) zt=(zt-1)%24;
3945 else zt=to_int(str)%24;
3946
3947 SetProp(P_TIMEZONE,zt);
3948
3949 if(!zt)
3950 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
3951 "eingestellt.\n");
3952 else if(zt>0)
3953 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
3954 "eingestellt.\n",zt);
3955 else
3956 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
3957 "Berlin eingestellt.\n",-zt);
3958 return 1;
3959}
3960
3961static int emailanzeige(string str){
3962 notify_fail("Syntax: emailanzeige [alle|freunde|niemand]\n");
3963 if(!str || str==""){
3964 if(!(str=QueryProp(P_SHOWEMAIL)))str="Niemandem";
3965 else if(str=="alle")str="allen";
3966 else if(str=="freunde")str="Deinen Freunden";
3967 else if(str=="niemand")str="niemandem";
3968 else{
3969 SetProp(P_SHOWEMAIL,0);
3970 str="Niemandem";
3971 }
3972 write("Deine Email wird "+str+" angezeigt.\n");
3973 return 1;
3974 }
3975 else if(member(({"alle","freunde","niemand"}),str)==-1)return 0;
3976
3977 SetProp(P_SHOWEMAIL,str);
3978
3979 if(str=="alle")str="allen";
3980 else if(str=="freunde")str="Deinen Freunden";
3981 else str="niemandem";
3982 write("Deine Email wird "+str+" angezeigt.\n");
3983 return 1;
3984}
3985
3986static int zaubertraenke()
3987{
3988 More("/room/orakel"->TipListe());
3989 return 1;
3990}
3991
3992varargs static int angriffsmeldung(string arg) {
3993 if (arg=="ein" || arg=="an")
3994 SetProp(P_SHOW_ATTACK_MSG,1);
3995 else if (arg=="aus")
3996 SetProp(P_SHOW_ATTACK_MSG,0);
3997 if (QueryProp(P_SHOW_ATTACK_MSG))
3998 write("Du siehst saemtliche Angriffsmeldungen von Dir.\n");
3999 else
4000 write("Du siehst nur neue Angriffsmeldungen von Dir.\n");
4001 return 1;
4002}
4003
4004static mixed _query_localcmds()
4005{
4006 return ({({"zeilen","set_screensize",0,0}),
4007 ({"email","set_email",0,0}),
4008 ({"url","set_homepage",0,0}),
4009 ({"icq","set_icq",0,0}),
4010 ({"messenger", "set_messenger", 0, 0}),
4011 ({"ort","set_location",0,0}),
4012 ({"punkte","short_score",0,0}),
4013 ({"score","short_score",0,0}),
4014 ({"info","score",0,0}),
4015 ({"kurzinfo","very_short_score",0,0}),
4016 ({"quit","new_quit",0,0}),
4017 ({"ende","new_quit",0,0}),
4018 ({"disconnect","disconnect",0,0}),
4019 ({"schlaf","disconnect",1,0}),
4020 ({"speichern","save_character",0,0}),
4021 ({"save","save_character",0,0}),
4022 ({"toete","kill",0,0}),
4023 ({"angriffsmeldung","angriffsmeldung",0,0}),
4024 ({"passw","change_password",1,0}),
4025 ({"hilfe","help",1,0}),
4026 ({"selbstloeschung","self_delete",0,0}),
4027 ({"spielpause","spielpause",0,0}),
4028 ({"spieldauer","spieldauer",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004029 ({"idee","ReportError",0,0}),
4030 ({"typo","ReportError",0,0}),
4031 ({"bug","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004032 ({"fehler","fehlerhilfe",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004033 ({"md","ReportError",0,0}),
4034 ({"detail","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004035 ({"vorsicht","toggle_whimpy",0,0}),
4036 ({"stop","stop",0,0}),
4037 ({"kwho","kwho",0,0}),
4038 ({"kwer","kwho",0,0}),
4039 ({"kkwer","kkwho",0,0}),
4040 ({"kkwho","kkwho",0,0}),
4041 ({"who","who",0,0}),
4042 ({"wer","who",0,0}),
4043 ({"zeit","uhrzeit",0,0}),
4044 ({"uhrzeit","uhrzeit",0,0}),
4045 ({"weg","weg",0,0}),
4046 ({"wegmeldung", "wegmeldung", 0, 0}),
4047 ({"idlezeit", "idlezeit", 0, 0}),
4048 ({"finger","finger",0,0}),
4049 ({"muds","muds",0,0}),
4050 ({"emote","emote",0,0}),
4051 ({":","emote",1,0}),
4052 ({";","emote",1,0}),
4053 ({"remote","remote",0,SEER_LVL}),
4054 ({"r:","remote",1,0}),
4055 ({"r;","gremote",1,0}),
4056 ({"titel","set_title",0,0}),
4057 ({"review","review",0,SEER_LVL}),
4058 ({"setmin","setmin",0,SEER_LVL}),
4059 ({"setmout","setmout",0,SEER_LVL}),
4060 ({"setmmin","setmmin",0,SEER_LVL}),
4061 ({"setmmout","setmmout",0,SEER_LVL}),
4062 ({"sethands","sethands",0,SEER_LVL}),
4063 ({"presay","presay",0,SEER_LVL}),
4064 ({"extralook","extralook",0,SEER_LVL}),
4065 ({"fluchtrichtung","toggle_whimpy_dir",0,SEER_LVL}),
4066 ({"inform","inform",0,0}),
4067 ({"erwarte","erwarte",0,0}),
4068 ({"stty","stty",0,0}),
4069 ({"grafik", "set_ascii_art", 0, 0}),
4070 ({"uhrmeldung","uhrmeldung",0,0}),
4071 ({"zeitzone","zeitzone",0,0}),
4072 ({"behalte","behalte",0,0}),
4073 ({"zweitiemarkierung","zweitiemarkierung",0,0}),
4074 ({"emailanzeige","emailanzeige",0,0}),
4075 ({"topliste","topliste",0,0}),
4076 ({"ton","set_visualbell",0,0}),
4077 ({"telnegs","show_telnegs",0,0}),
4078 ({"spotte", "spotte", 0, 0}),
4079 ({"reise","reise",0,0}),
4080 ({"zaubertraenke","zaubertraenke",0,0}),
4081 ({"telnet","telnet_cmd",0,0}),
4082 })+
4083 command::_query_localcmds()+
4084 viewcmd::_query_localcmds()+
4085 comm::_query_localcmds()+
4086 skills::_query_localcmds()+
4087 description::_query_localcmds();
4088}
4089
4090static int _check_keep(object ob)
4091{
4092 return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
4093}
4094
4095static mixed _set_testplayer(mixed arg) {
4096 mixed res;
4097 object setob;
4098
4099 setob=this_player();
4100 if (!objectp(setob) || !query_once_interactive(setob))
4101 setob=this_interactive();
4102 if (!objectp(setob))
4103 setob=previous_object();
4104 if (setob && !IS_DEPUTY(setob)) {
4105 arg=geteuid(setob);
4106 if (!arg || arg=="NOBODY")
4107 arg=getuid(setob);
4108 arg=capitalize(arg);
4109 }
4110 res=Set(P_TESTPLAYER,arg);
4111 Set(P_TESTPLAYER,PROTECTED,F_MODE_AS);
4112 return res;
4113}
4114
4115int zweitiemarkierung(string arg)
4116{
4117 if (!QueryProp(P_SECOND))
4118 return _notify_fail("Aber Du bist doch gar kein Zweiti.\n"),0;
4119 notify_fail("Syntax: zweitiemarkierung [unsichtbar|sichtbar|name]\n");
4120 if (!arg)
4121 return 0;
4122 switch (arg)
4123 {
4124 case "unsichtbar" :
4125 SetProp(P_SECOND_MARK,-1);
4126 write("Jetzt sieht kein Spieler mehr, dass Du ein Zweiti bist.\n");
4127 return 1;
4128 case "sichtbar" :
4129 SetProp(P_SECOND_MARK,0);
4130 write("Jetzt sieht kein Spieler mehr, wessen Zweiti Du bist.\n");
4131 return 1;
4132 case "name" :
4133 SetProp(P_SECOND_MARK,1);
4134 write("Jetzt koennen alle sehen, wessen Zweiti Du bist.\n");
4135 return 1;
4136 }
4137 return 0;
4138}
4139
4140int topliste(string arg)
4141{
4142 if (!arg)
4143 {
4144 printf("Du hast Dich fuer die Topliste %s.\n",
4145 (QueryProp(P_NO_TOPLIST) ? "gesperrt" : "freigegeben"));
4146 return 1;
4147 }
4148 else if (member(({"j","ja","n","nein"}),arg)==-1)
4149 return _notify_fail("Syntax: topliste [ja|nein]\n"),0;
4150 if (arg[0]=='j')
4151 {
4152 SetProp(P_NO_TOPLIST,0);
4153 write("Du kannst jetzt (theoretisch) in der Topliste auftauchen.\n");
4154 }
4155 else
4156 {
4157 SetProp(P_NO_TOPLIST,1);
Zesstradd2d1982017-01-28 14:03:19 +01004158 "/secure/topliste"->DeletePlayer();
4159 write("Du wirst jetzt nicht (mehr) in den Toplisten auftauchen.\n");
MG Mud User88f12472016-06-24 23:31:02 +02004160 }
4161 Set(P_NO_TOPLIST,SAVE|PROTECTED,F_MODE_AS);
4162 return 1;
4163}
4164
4165int show_telnegs(string arg)
4166{
4167 if (!arg)
4168 {
4169 write("Du bekommst Aenderungen Deiner Fenstergroesse "+
4170 (QueryProp(P_TTY_SHOW)?"":"nicht ")+"angezeigt.\n");
4171 return 1;
4172 }
4173 if (member(({"ein","an","aus"}),arg)==-1)
4174 {
4175 write("Syntax: telnegs [ein|aus]\n");
4176 return 1;
4177 }
4178 if (arg=="ein" || arg=="an")
4179 {
4180 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"":"nun ")+
4181 "Aenderungen Deiner Fenstergroesse angezeigt.\n");
4182 Set(P_TTY_SHOW,1);
4183 return 1;
4184 }
4185 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"nun ":"")+
4186 "Aenderungen Deiner Fenstergroesse nicht "+
4187 (QueryProp(P_TTY_SHOW)?"mehr ":"")+"angezeigt.\n");
4188 Set(P_TTY_SHOW,0);
4189 return 1;
4190}
4191
4192private int set_keep_alive(string str) {
4193 if (str == "ein") {
4194 telnet_tm_counter = 240 / __HEART_BEAT_INTERVAL__;
4195 tell_object(this_object(), break_string(
4196 "An Deinen Client werden jetzt alle 4 Minuten unsichtbare Daten "
4197 "geschickt, um zu verhindern, dass Deine Verbindung zum "MUDNAME
4198 " beendet wird.", 78));
4199 }
4200 else if (str == "aus") {
4201 telnet_tm_counter = 0;
4202 tell_object(this_object(),break_string(
4203 "Du hast das Senden von unsichtbaren Daten (Keep-Alive-Pakete) an "
4204 "Deinen Client ausgeschaltet.",78));
4205 }
4206 else {
4207 if (!telnet_tm_counter)
4208 tell_object(this_object(), break_string(
4209 "An Deinen Client werden keine Keep-Alive-Pakete geschickt.",78));
4210 else
4211 tell_object(this_object(), break_string(
4212 "An Deinen Client werden alle 4 Minuten "
4213 "unsichtbare Daten geschickt, damit Deine Verbindung "
4214 "zum "MUDNAME" nicht beendet wird.",78));
4215 }
4216 return 1;
4217}
4218
4219private int print_telnet_rttime() {
4220 int rtt = QueryProp(P_TELNET_RTTIME);
4221 if (rtt>0)
4222 tell_object(ME, break_string(
4223 "Die letzte gemessene 'round-trip' Zeit vom MG zu Deinem Client "
4224 "und zurueck betrug " + rtt + " us.",78));
4225 else
4226 tell_object(ME, break_string(
4227 "Bislang wurde die 'round-trip' Zeit vom MG zu Deinem Client "
4228 "noch nicht gemessen oder Dein Client unterstuetzt dieses "
4229 "nicht.",78));
4230 return 1;
4231}
4232
4233int telnet_cmd(string str) {
4234 if (!str) return 0;
4235 string *args = explode(str, " ");
4236 string newargs;
4237 if (sizeof(args) > 1)
4238 newargs = implode(args[1..], " ");
4239 else
4240 newargs = "";
4241
4242 switch(args[0])
4243 {
4244 case "keepalive":
4245 return set_keep_alive(newargs);
4246 case "rttime":
4247 return print_telnet_rttime();
4248 }
4249 return 0;
4250}
4251
4252int spotte( string str )
4253{
4254 _notify_fail( "Hier ist nichts, was Du verspotten koenntest!\n" );
4255 return 0;
4256}
4257
4258int behalte(string str)
4259{
4260 object ob,*obs;
4261 string s;
4262
4263 if (str)
4264 {
4265 if (str=="alles") {
4266 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, getuid());
4267 write("Ok!\n");
4268 return 1;
4269 }
4270 if (str=="nichts") {
4271 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, 0);
4272 write("Ok!\n");
4273 return 1;
4274 }
4275 if (!sizeof(obs=find_obs(str,PUT_GET_NONE)))
4276 {
4277 _notify_fail("Aber sowas hast Du nicht dabei!\n");
4278 return 0;
4279 }
4280 else ob=obs[0];
4281
4282 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4283 ob->SetProp(P_KEEP_ON_SELL,0);
4284 else
4285 ob->SetProp(P_KEEP_ON_SELL,geteuid(ME));
4286
4287 // erneut abfragen, da sich der Wert nicht geaendert haben muss
4288 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4289 write(break_string(sprintf("Ok, Du wirst %s jetzt bei 'verkaufe alles' "
4290 "behalten.\n",ob->name(WEN)),78));
4291 else
4292 write(break_string(sprintf("Ok, Du wirst %s beim naechsten 'verkaufe "
4293 "alles' mitverkaufen!\n",ob->name(WEN)),78));
4294
4295 return 1;
4296 }
4297 s=make_invlist(ME,filter(all_inventory(ME),#'_check_keep)); //'));
4298 More(s);
4299 return 1;
4300}
4301
4302static int _query_lep()
4303{
4304 int val;
4305 val = LEPMASTER->QueryLEP();
4306 Set( P_LEP, val );
4307 return val;
4308}
4309
4310static mixed _set_fraternitasdonoarchmagorum(mixed arg)
4311{
4312 if (!intp(arg)) return -1;
4313
4314 if ((!previous_object(1)||object_name(previous_object(1))!=FAO_MASTER) &&
4315 (!this_interactive() || !IS_ARCH(this_interactive())))
4316 return -1;
4317
4318 if (!intp(arg)) return -1;
4319
4320 log_file("fao/P_FAO",sprintf("%s - %s P_FAO gesetzt auf %O\n",
4321 dtime(time()),query_real_name(),arg) );
4322 return Set(P_FAO,arg);
4323}
4324
4325nomask void set_realip(string str)
4326{
4327 if(previous_object() && strstr(object_name(previous_object()),"/secure")==0)
4328 {
4329 realip=str;
4330 }
4331}
4332
4333nomask string query_realip()
4334{
4335 return realip ? realip : "";
4336}
4337
4338mixed _query_netdead_env() {
4339 return ndead_location || ndead_l_filename;
4340}