blob: 9d78006b0ae6194c6f16ddd4849de60661664b65 [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:
Zesstra4eb67dc2016-12-17 19:56:31 +01002065 ReceiveMsg(capitalize(destpl->name()) + "->" + str, MT_COMM|MT_FAR,
2066 MA_EMOTE, 0, ME);
MG Mud User88f12472016-06-24 23:31:02 +02002067 break;
2068 case MSG_BUFFERED:
2069 write( capitalize(destpl->name(WER) + " ist gerade beschaeftigt.\n") );
2070 break;
2071 case MSG_IGNORED:
2072 write( capitalize(destpl->name(WER) + " ignoriert Dich.\n") );
2073 break;
2074 case MSG_VERB_IGN:
2075 case MSG_MUD_IGN:
2076 write( capitalize(destpl->name(WER) + " ignoriert Deine Meldung.\n") );
2077 break;
2078 default:
2079 write( capitalize(destpl->name(WER) + " kann Dich gerade nicht "
2080 "wahrnehmen.\n") );
2081 }
2082 return 1;
2083}
2084
2085/** Spielerkommando fuers emoten im Genitiv ';'.
2086 * Ruft emote(string,int) auf.
2087 */
2088static int gemote(string str)
2089{
2090 return emote(str, 1);
2091}
2092
2093/** Spielerkommando fuers remoten im Genitiv 'r;'.
2094 * Ruft remote(string, int) auf.
2095 */
2096static int gremote(string str)
2097{
2098 return remote(str, 1);
2099}
2100
2101static void load_auto_objects(mapping map_ldfied);
2102
2103private void InitPlayer();
2104private void InitPlayer2();
2105private void InitPlayer3();
2106
2107/** Gibt eine Zufallszahl um P_AVERAGE_SIZE herum zurueck.
2108 * \return Zufaellige Groesse.
2109 */
2110private int RandomSize()
2111{
2112 return (100+random(13)-random(13)+random(13)-random(13))*
2113 (QueryProp(P_AVERAGE_SIZE)||170)/100;
2114}
2115
2116/** Setzt bestimmte Props im Spieler, falls diese nicht gesetzt sind oder
2117 * loescht obsolete Props. Repariert bestimmte Datenstrukturen im Spieler.
2118 * Wird von start_player() nach Laden des Savefiles gerufen.
2119 * Momentan wird z.B. die Groesse gesetzt, falls sie bisher 0 ist und die
2120 * Skills des Spielers initialisiert bzw. repariert oder auf die aktuellste
2121 * Version des Skillsystems
2122 * Ruft ggf. InitSkills() und FixSkills().
2123 * @param[in] newflag Gibt an, ob es ein neuerstellter Spieler ist.
2124 * @sa start_player(), InitSkills(), FixSkills()
2125 */
2126private void updates_after_restore(int newflag) {
2127
2128 // Seher duerfen die Fluchtrichtung uebermitteln lassen.
2129 // Eigentlich koennte es Merlin machen. Dummerweise gibt es ja auch alte
2130 // Seher und dann kann es gleiche fuer alle hier gemacht werden. (Ob der
2131 // Code jemals rauskann?)
2132 //TODO: Irgendwann alle Seher korrigieren und Code nach Merlin schieben...
2133 if (IS_SEER(ME))
2134 SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS) | CAN_REPORT_WIMPY_DIR);
2135
2136 // ggf. Invis-Eigenschaft aus dem Loginobjekt abrufen (Invislogin), koennte
2137 // ja anders als aus Savefile sein. Gesetztes P_INVIS aus diesem aber
2138 // beibehalten.
2139 if (IS_LEARNER(ME) && !QueryProp(P_INVIS)
2140// && load_name(previous_object()) == "/secure/login"
2141 )
2142 {
2143 SetProp(P_INVIS, previous_object()->query_invis());
2144 if (QueryProp(P_INVIS))
2145 tell_object(ME, "DU BIST UNSICHTBAR!\n" );
2146 }
2147 "*"::updates_after_restore(newflag);
2148
2149 attributes::UpdateAttributes();
2150
2151 int size=Query(P_SIZE);
2152 if (!size) size=RandomSize();
2153 while(size==QueryProp(P_AVERAGE_SIZE))
2154 size=RandomSize();
2155 Set(P_SIZE,size);
2156
2157 // Prop wird nicht mehr genutzt. TODO: irgendwann entfernen.
2158 Set(P_SECOND_LIST, SAVE, F_MODE_AD);
2159 Set(P_SECOND_LIST, 0, F_VALUE);
2160}
2161
2162
2163/** Setzt den HC-Modus.
2164 */
2165varargs nomask void set_hc_play(string str,int val)
2166{
2167 string str1;
2168
2169 str1 = explode( object_name(previous_object()), "#" )[0];
2170
2171 if ( str1 != "/secure/login" &&
2172 previous_object()!=this_object() &&
2173 extern_call() &&
2174 (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
2175 capitalize(geteuid(ME)) != str ||
2176 geteuid(ME) != geteuid(previous_object())) ){
2177 write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
2178 return;
2179 }
2180
2181 hc_play=val;
2182}
2183
2184/** gibt den HC-Modus zurueck.
2185 */
2186nomask int query_hc_play()
2187{
2188 return hc_play;
2189}
2190
2191/** Initialisiert und aktiviert das Spielerobjekt.
2192 * Kann nur von /secure/login oder /secure/master gerufen werden.
2193 * Startet Telnet Negotiation, laedt Savefile, setzt einige Props.
2194 * Ruft updates_after_restore(), um div. Daten zu aktualisieren/reparieren.
2195 * Ruft create() aus /std/player/potion.c.
2196 * Bei neuem Spieler wird der entsprechende Event ausgeloest die Attribute
2197 * auf die Startwerte gesetzt.
2198 * Prueft Zweitiemarkierungen.
2199 * Ruft InitPlayer().
2200 * @param[in] str Name des Charakters, der geladen werden soll.
2201 * @param[in] ip textuelle Repraesentation der IP-Adresse des Spielers.
2202 * @return 1 bei Erfolg, 0 sonst.
2203 * @todo Div. Reparaturen/Updates nach updates_after_restore() auslagern.
2204 */
2205varargs nomask int start_player( string str, string ip )
2206{
2207 mixed second;
2208 int newflag; /* could player be restored? */
2209 string str1;
2210
2211 call_out( "disconnect", 600 );
2212
2213 str1 = explode( object_name(previous_object()), "#" )[0];
2214
2215 if ( str1 != "/secure/login" &&
2216 str1 != "/secure/master" &&
2217 (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
2218 capitalize(geteuid(ME)) != str ||
2219 geteuid(ME) != geteuid(previous_object())) ){
2220 write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
2221 destruct(ME);
2222 return 0;
2223 }
2224
2225 /* try to restore player. If it doesn't exist, set the new flag */
Zesstraf3f22662017-01-30 15:54:29 +01002226 newflag = !restore_object( SAVEPATH + lower_case(str)[0..0] + "/"
MG Mud User88f12472016-06-24 23:31:02 +02002227 +lower_case(str) );
2228
2229 updates_after_restore(newflag);
2230
2231 if ( query_once_interactive(ME) )
2232 {
2233 // Telnet-Negotiations durchfuehren, aber nur die grundlegenden aus
2234 // telnetneg. Alle anderen sollten erst spaeter, nach vollstaendiger
2235 // Initialisierung gemacht werden.
2236 telnetneg::startup_telnet_negs();
2237 modify_prompt();
2238 Set( P_LAST_LOGIN, time() );
2239 }
2240
2241 Set( P_WANTS_TO_LEARN, 1 ); // 1 sollte der default sein !!!
2242 Set( P_WANTS_TO_LEARN, PROTECTED, F_MODE_AS );
2243 // Eingefuegt 18.11.99, kann nach einem Jahr wieder raus:
2244 Set( P_TESTPLAYER, PROTECTED, F_MODE_AS );
2245
2246 if ( IS_LEARNER(ME) )
2247 SetProp( P_CAN_FLAGS, QueryProp(P_CAN_FLAGS)|CAN_REMOTE );
2248
2249 Set( P_NAME, str );
2250 Set( P_NAME, SECURED, F_MODE_AS );
2251
2252 if ( !QueryProp(P_NEEDED_QP) )
2253 SetProp( P_NEEDED_QP, REQ_QP );
2254
2255 Set( P_NEEDED_QP, NOSETMETHOD, F_SET_METHOD );
2256 Set( P_NEEDED_QP, SAVE|SECURED, F_MODE_AS );
2257
2258 /* autosave the player after 500 heartbeats */
2259 time_to_save = age + 500;
2260 potion::create(); /* DO IT HERE AFTER THE RESTORE !! */
2261
2262 AddId( getuid() );
2263 SetProp( P_AC, 0 );
2264 SetProp( P_WEAPON, 0 );
2265
2266 /* Set some things which wont be set when all is OK */
2267 SetProp( P_MAX_HP, (QueryAttribute(A_CON) * 8 + 42 ));
2268 SetProp( P_MAX_SP, (QueryAttribute(A_INT) * 8 + 42 ));
2269
2270 catch( bb = "/secure/bbmaster"->query_bb() );
2271
2272 /* If this is a new character, we call the adventurers guild to get
2273 * our first title !
2274 */
2275 if ( newflag ) {
2276 if ( QueryGuest())
2277 SetProp( P_TITLE, "ueberkommt das "MUDNAME" ..." );
2278
2279 Set( P_LEVEL, -1 );
2280 SetProp( P_ATTRIBUTES, ([ A_STR:1, A_CON:1, A_INT:1, A_DEX:1 ]) );
2281 SetProp( P_HP, QueryProp(P_MAX_HP) );
2282
2283 // Event ausloesen
2284 EVENTD->TriggerEvent(EVT_LIB_PLAYER_CREATION, ([
2285 E_OBJECT: ME,
2286 E_PLNAME: getuid(ME) ]) );
2287 }
2288
2289 InitPlayer();
2290
2291 // Padreic 01.02.1999
Zesstra85576452017-01-30 15:43:21 +01002292 if ( !IS_LEARNER(ME) && (second = QueryProp(P_SECOND)) )
2293 {
2294 if ( stringp(second) && lower_case(second)[0..3] == "von " )
2295 {
MG Mud User88f12472016-06-24 23:31:02 +02002296 second = lower_case(second[4..]);
2297 SetProp( P_SECOND, second );
2298 }
2299
2300 if ( !stringp(second ) ||
Zesstra85576452017-01-30 15:43:21 +01002301 !master()->find_userinfo(second))
2302 {
2303 // Wenns nur an Gross-/Kleinschreibung liegt, wird automatisch
2304 // korrigiert.
MG Mud User88f12472016-06-24 23:31:02 +02002305 if ( stringp(second) &&
Zesstra85576452017-01-30 15:43:21 +01002306 master()->find_userinfo(lower_case(second)))
2307 {
MG Mud User88f12472016-06-24 23:31:02 +02002308 SetProp( P_SECOND, lower_case(second) );
2309 log_file( "WRONG_SECOND",
2310 sprintf( "%s: %s: P_SECOND = %O -> Automatisch "
2311 "korrigiert,\n",
2312 dtime(time()), object_name(), second ) );
2313 }
2314 else {
2315 tell_object( ME,
2316 "*\n*\n* Deine Zweitiemarkierung ist ungueltig, "
2317 "bitte aendere diese und sprich im\n* Zweifel "
2318 "bitte einen Erzmagier an.\n*\n*\n" );
2319
2320 log_file( "WRONG_SECOND",
2321 sprintf( "%s: %s: P_SECOND = %O\n",
2322 dtime(time()), object_name(), second ) );
2323 // ein bisschen deutlicher auffordern.. Padreic 08.04.1999
2324 move( "/d/gebirge/room/zwafflad", M_GO );
2325 }
2326 }
2327 }
2328 return(0);
2329}
2330
2331/** Letzte Phase der Spielerinitialisierung beim Laden des Charakters.
2332 * Ruft enable_commands(), aktiviert den Heartbeat und aktiviert die
2333 * Kommandos aus den geerbten command.c, put_and_get.c, team.c, soul.c,
2334 * guide.c, setzt den Living Name.
2335 * Registriert UseSpell() als Catchall-Kommando.
2336 * Laesst den Spieler ggf. einen Level per /std/gilde aufsteigen, ruft
2337 * call_notify_player_change(), loest Login-Event aus.
2338 * Gibt Willkommenstexte, News und neue Mails aus.
2339 * Findet den Startraum und bewegt den Spieler dorthin.
2340 * Ruft FinalSetup() (aus den Rassenshells).
2341 * Begrenzt Geldmenge im Spieler (wegen Bankzweities) nach Tragkraft und
2342 * erstattet Geld bei Reboot.
2343 * Ruft set_is_wizard().
2344 * Startet Clonen der Autoloader.
2345 * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
2346 */
2347private void InitPlayer4()
2348{
2349 int num, str, ski;
2350 string err, called_from_ip;
2351 mixed start_place;
2352 object mon;
2353
2354 enable_commands();
2355 set_heart_beat(1);
2356 command::initialize();
2357 add_put_and_get_commands();
2358 add_team_commands();
2359 add_soul_commands();
2360 add_guide_commands();
2361 add_action( "UseSpell", "", 1 );
2362 set_living_name( getuid() );
2363 while ( remove_call_out("disconnect") != -1 )
2364 ;
2365
2366 if ( QueryProp(P_LEVEL) == -1 )
2367 {
2368 catch( "/std/gilde"->try_player_advance(this_object()) ;publish );
2369 }
2370
2371 if ( interactive(ME) )
2372 call_notify_player_change(1);
2373
2374 if ( interactive(this_object()) ) {
2375 cat( "/etc/NEWS" );
2376
2377 NewbieIntroMsg();
2378
2379 if ( QueryProp(P_INVIS) && !IS_WIZARD(ME) )
2380 SetProp( P_INVIS, 0 );
2381
2382 catch( num = "secure/mailer"->FingerMail(getuid());publish );
2383
2384 if ( num )
2385 write( "Du hast " + num + " neue" +
2386 (num == 1 ? "n Brief" : " Briefe")+" im Postamt liegen.\n" );
2387
2388 catch( RegisterChannels();publish );
2389
2390 if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
2391 query_ip_number(ME) != called_from_ip ){
2392 string tmp;
2393
2394 if ( stringp(tmp = query_ip_name(called_from_ip)) &&
2395 tmp != called_from_ip )
2396 tmp = " [" + tmp + "]";
2397 else
2398 tmp = "";
2399
2400 write( "Das letzte Mal kamst Du von " + called_from_ip
2401 + tmp + ".\n" );
2402 }
2403
2404 Set( P_CALLED_FROM_IP, query_ip_number(ME) );
2405 }
2406
2407 if ( !stringp(default_home) || default_home == "" )
2408 default_home = "/gilden/abenteurer";
2409
2410 if ( IS_SEER(ME) && !IS_LEARNER(ME) )
2411 catch( start_place = HAUSVERWALTER->FindeHaus(getuid(ME));publish );
2412 // wenn der Spieler noch ganz frisch ist und noch wenig Stufenpunkte
2413 // gekriegt hat und das Tutorial noch nicht gemacht hat, startet er im
2414 // Tutorial.
2415 else if (QueryProp(P_LEP) <= 120
2416 && QM->HasMiniQuest(this_object(),
2417 "/d/anfaenger/ennox/tutorial/npcs/verkaeufer") != 1)
2418 start_place = "/room/welcome/"+ getuid(this_object());
2419 else
2420 start_place = 0;
2421
2422 if ( !start_place )
2423 start_place = QueryProp(P_START_HOME);
2424
2425 if( objectp(start_place) || (stringp(start_place) && start_place != "" ) ){
2426 if ((err = catch(move( start_place, M_GO|M_SILENT|M_NO_SHOW );publish))
2427 || !environment() )
2428 err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
2429 }
2430 else
2431 err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
2432
2433 if ( err )
2434 catch(move( "/gilden/abenteurer", M_GO|M_SILENT|M_NO_SHOW );publish);
2435
2436 // Die Shell muss FinalSetup() nicht implementieren. Daher Callother
2437 catch( ME->FinalSetup();publish );
2438
2439 // Login-event ausloesen
2440 EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
2441 E_OBJECT: ME,
2442 E_PLNAME: getuid(ME),
2443 E_ENVIRONMENT: environment() ]) );
2444
2445 // erst jetzt GMCP freigeben und zu verhandeln.
2446 gmcp::startup_telnet_negs();
2447
2448 // Schonmal sichern, falls ein Bug (Evalcost...) dazwischen kommen sollte.
2449 autoload_rest = autoload;
2450
2451 // Um Geld-Xties mit illegal viel Geld zu vermeiden, kommt ein Check:
2452 if ( !IS_LEARNER(ME) && !Query(P_TESTPLAYER) &&
2453 mon = present( "\ngeld", ME ) ){
2454 // maximale Kraft, die der Spieler haette haben koennen
2455 str = QueryAttribute(A_STR) + 4;
2456 if ( Query(P_FROG) )
2457 str += 30;
2458 if ( str < 1 )
2459 str = QueryRealAttribute(A_STR) + 4;
2460 if ( str > 30 )
2461 str = 30;
2462
2463 // Trageskill beachten
2464 ski = to_int(UseSkill( SK_CARRY, ([SI_SKILLARG : str ]) ));
2465 if ( !intp(ski) )
2466 ski = 0;
2467
2468 // Wieviel konnte der Spieler insgesamt maximal tragen?
2469 num = 9200 + str * 800 + ski;
2470 if ( num < 3000 )
2471 num = 3000;
2472
2473 // Verdoppeln fuer einen guten Container und nochmal 20% draufschlagen
2474 // zur Sicherheit. Das Ganze danach *4, um die maximale Anzahl Muenzen
2475 // zu erhalten.
2476 num = (int) (num * 8.8);
2477
2478 // Geld, das zuviel ist, auf den Maximalwert kuerzen.
2479 // Zur Sicherheit wird mitgeloggt. Falls ein Spieler sich (zu recht)
2480 // beschwert, kann man immer noch wieder die Summe korrigieren ;-)
2481 if ( (str = mon->QueryProp(P_AMOUNT)) > num ){
2482 mon->SetProp( P_AMOUNT, num );
2483 log_file( "ZUVIEL_GELD", sprintf( "%s: %s hatte %d Muenzen bei "
2484 "sich. Korrigiert auf %d.\n ",
2485 ctime(time())[4..],
2486 capitalize(getuid(ME)),
2487 str, num ) );
2488 }
2489 }
2490 int entschaedigung = QueryProp(P_CARRIED_VALUE);
2491 if ( entschaedigung > 0 )
2492 {
2493 write( "Du findest " + entschaedigung +
2494 " Muenzen, die Du beim letzten Mal verloren hast.\n" );
2495
2496 if ( MayAddWeight( entschaedigung / 4 ) ){
2497 write( "Weil Du nicht mehr soviel tragen kannst, spendest Du den "+
2498 "Rest der Zentralbank.\n" );
2499
2500 num = (QueryProp(P_MAX_WEIGHT) - query_weight_contents()) * 4;
2501 entschaedigung = (num < 0) ? 0 : num;
2502 }
2503
2504 AddMoney( entschaedigung );
2505 SetProp(P_CARRIED_VALUE,0);
2506 }
2507
2508 if ( !QueryProp(P_INVIS) )
2509 say( capitalize(name(WER)) + " betritt diese Welt.\n" );
2510 else
2511 write( "DU BIST UNSICHTBAR!\n\n" );
2512#if __EFUN_DEFINED__(set_is_wizard)
2513 if ( IS_WIZARD(getuid(ME)) )
2514 set_is_wizard( ME, 1 );
2515 else
2516 set_is_wizard( ME, 0 );
2517#endif
2518 if ( query_once_interactive(ME) )
2519 ListAwaited();
2520
2521 // Autoloader werden ganz zum Schluss geclont, da das bis zum bitteren
2522 // (Evalcost-)Ende geschieht und danach u.U. keine Rechenzeit fuer
2523 // andere Aktionen mehr ueber ist
2524 load_auto_objects( autoload );
2525}
2526
2527/** Setzt die Spielerinitialisierung nach start_player() fort.
2528 * Prueft den Wert der vom Spieler getragenen Sachen (fuer Entschaedigung
2529 * nach Reboot).
2530 * Setzt div. Properties des "Spielerkoerpers", die eigentlich gespeichert
2531 * werden, nach einem Reboot zurueck.
2532 * Fragt ggf. nach eMail-Adresse und uebergibt per input_to an
2533 * InitPlayer2().
2534 * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
2535*/
2536private void InitPlayer()
2537{
2538 string mailaddr;
2539
2540 // wenn es einen Crash gab, sollen Spieler nicht noch extra bestraft werden
2541 if ( file_time( "/save/" + getuid()[0..0] + "/" + getuid() + ".o" )
2542 < last_reboot_time() ){
2543 SetProp( P_FOOD, 0 );
2544 SetProp( P_DRINK, 0 );
2545 SetProp( P_ALCOHOL, 0 );
2546 SetProp( P_BLIND, 0 );
2547 SetProp( P_DEAF, 0 );
2548 SetProp( P_POISON, 0 );
2549 SetProp( P_GHOST, 0 );
2550 SetProp( P_FROG, 0 );
2551 SetProp( P_HP, QueryProp(P_MAX_HP) );
2552 SetProp( P_SP, QueryProp(P_MAX_SP) );
2553 }
2554
2555 if ( QueryGuest() )
2556 Set( P_MAILADDR, "none" );
2557 else if ( !(mailaddr = Query(P_MAILADDR)) || mailaddr == "" ) {
2558 write(break_string(
2559 "Eine gueltige EMail-Adresse erleichtert es erheblich, Dir "
2560 "ein neues Passwort setzen zu lassen, falls Du einmal Dein "
2561 "Passwort vergisst.",78));
2562 input_to( "getmailaddr",INPUT_PROMPT,
2563 "Gib bitte Deine EMail-Adresse an: " );
2564 return;
2565 }
2566 InitPlayer2();
2567}
2568
2569/** liest eMail-Adresse vom Spieler ein und speichert sie.
2570 * Uebergibt anschliessend an InitPlayer2()
2571 * @param[in] maddr Spielereingabe der Emailadresse.
2572 * @sa InitPlayer2().
2573 */
2574static void getmailaddr( string maddr )
2575{
2576 maddr = check_email(maddr);
2577
2578 if ( !stringp(maddr)) {
2579 write("Deine Eingabe scheint keine gueltige EMail-Adresse gewesen "
2580 "zu sein.\n");
2581 input_to( "getmailaddr", INPUT_PROMPT,
2582 "Gib bitte Deine EMail-Adresse an: " );
2583 return;
2584 }
2585 Set( P_MAILADDR, maddr );
2586 InitPlayer2();
2587}
2588
2589
2590/** Prueft Geschlecht des Spielers und fragt ggf. beim Spieler nach.
2591 * Uebergibt an InitPlayer3() oder get_gender().
2592 * @sa InitPlayer3(), get_gender().
2593 */
2594private void InitPlayer2()
2595{
2596 if( member(({ MALE, FEMALE }), QueryProp(P_GENDER) ) == -1 ) {
2597 input_to( "getgender", INPUT_PROMPT,
2598 "Bist Du maennlich oder weiblich: ");
2599 return;
2600 }
2601
2602 InitPlayer3();
2603}
2604
2605/** Liest Spielerantwort auf die Frage nach dem Geschlecht des Chars ein.
2606 * Wird gerufen von input_to().
2607 * Uebergibt an InitPlayer3().
2608 * @sa InitPlayer3().
2609 */
2610static void getgender( string gender_string )
2611{
2612 gender_string = lower_case( gender_string );
2613
2614 if ( sizeof(gender_string)==1 && gender_string[0] == 'm' ){
2615 write( "Willkommen, mein Herr!\n" );
2616 SetProp( P_GENDER, MALE );
2617 }
2618 else if ( sizeof(gender_string)==1 && gender_string[0] == 'w' ){
2619 write( "Willkommen, gnae' Frau!\n" );
2620 SetProp( P_GENDER, FEMALE );
2621 }
2622 else {
2623 write( "Wie? Was? Verstehe ich nicht!\n" );
2624 input_to( "getgender", INPUT_PROMPT,
2625 "Bist Du maennlich oder weiblich? (tippe m oder w): ");
2626 return;
2627 }
2628
2629 InitPlayer3();
2630}
2631
2632
2633/** Prueft Terminaltyp des Spielers und fragt ggf. beim Spieler nach.
2634 * Uebergibt an InitPlayer4() oder gettty().
2635 * @sa InitPlayer4(), gettty().
2636 */
2637private void InitPlayer3()
2638{
2639 if ( !QueryProp(P_TTY) || QueryProp(P_TTY) == "none" )
2640 {
2641 write( "Waehle einen Terminaltyp (kann spaeter mit <stty> geaendert "
2642 "werden)\n");
2643 input_to( "gettty", INPUT_PROMPT, "vt100, ansi, dumb (Standard: dumb): " );
2644 return;
2645 }
2646 InitPlayer4();
2647}
2648
2649/** Liest Spielerantwort auf die Frage nach dem Terminaltyp ein.
2650 * Wird gerufen von input_to().
2651 * Uebergibt an InitPlayer4().
2652 * @sa InitPlayer4().
2653 */
2654static void gettty( string ttystr )
2655{
2656 if ( !ttystr || ttystr == "" )
2657 ttystr = "dumb";
2658
2659 ttystr = lower_case(ttystr);
2660
2661 if ( ttystr == "vt100" ){
2662 write( "Dies sollte " + ANSI_BOLD + "fett" + ANSI_NORMAL + " sein.\n" );
2663 SetProp( P_TTY, ttystr );
2664 }
2665 else
2666 if ( ttystr == "ansi" ){
2667 write( "Dies sollte " + ANSI_RED + "rot" + ANSI_NORMAL +
2668 " sein.\n" );
2669 SetProp( P_TTY, "ansi" );
2670 }
2671 else if ( ttystr == "dumb" ){
2672 write( "Ohje, oede! Besorg Dir ein besseres Terminal!\n" );
2673 SetProp( P_TTY, "dumb" );
2674 }
2675 else {
2676 write( "Dieser Terminaltyp wird nicht unterstuetzt. Nimm bitte "
2677 "einen aus:\nvt100, ansi or dumb (Standard ist dumb).\n" );
2678 input_to( "gettty", INPUT_PROMPT,
2679 "vt100, ansi or dumb (Standard ist dumb): ");
2680 return;
2681 }
2682
2683 InitPlayer4();
2684}
2685
2686
2687/** Liefert die UID des Charakters zurueck, also den Charakternamen.
2688 * @return UID des Objekts (Charaktername).
2689 */
2690nomask string query_real_name() {
2691 /* ACHTUNG !! DIES LFUN DARF NICHT ENTFERNT WERDEN !!! */
2692 /* Sie wird vom Gamedriver (zB bei F_ED) aufgerufen !! */
2693 /* Ich bin da zwar nicht so ueberzeugt von, dass der Driver die heutzutage
2694 * noch ruft, aber die halbe Mudlib ruft sie. ;-) (Zesstra, 27.4.08)
2695 */
2696 return getuid();
2697}
2698
2699/*
2700 * the wizard command review: show player moving messages
2701 */
2702int review() {
2703 string *msg;
2704 write(short());
2705 write("Deine Bewegungen werden wie folgt gemeldet:\n"+
2706 "mout: "+name(WER)+" "+(msg=explode(QueryProp(P_MSGOUT),"#"))[0]
2707 +" <Richtung>"+(sizeof(msg)>1 ? msg[1] : "")+".\n"+
2708 "min: "+name(WER)+" "+QueryProp(P_MSGIN)+".\n"+
2709 "mmout: "+name(WER)+" "+QueryProp(P_MMSGOUT)+".\n"+
2710 "mmin: "+name(WER)+" "+QueryProp(P_MMSGIN)+".\n"+
2711 (IS_LEARNER(ME) ?
2712 "cmsg: "+name(WER)+" "+QueryProp(P_CLONE_MSG)+".\n"+
2713 "dmsg: <Irgendetwas> "+QueryProp(P_DESTRUCT_MSG)+".\n"
2714 : "")+
2715 "Wenn Du jemanden angreifst, sieht das so aus:\n"+
2716 name(WER)+" greift Dich"+QueryProp(P_HANDS)[0]+" an.\n");
2717 return 1;
2718}
2719
2720/*
2721 * set player moving messages
2722 */
2723
2724static int setmin(string str)
2725{
2726 SetProp(P_MSGIN, _unparsed_args()||"kommt an");
2727 write("Ok.\n");
2728 return 1;
2729}
2730
2731static int setmout(string str)
2732{
2733 string *msg;
2734
2735 if(sizeof(msg=explode((_unparsed_args()||"geht"),"#"))>2)
2736 {
2737 write("Du darfst nur einmal '#' fuer die Richtung angeben.\n");
2738 return 1;
2739 }
2740 if(sizeof(msg)>1)
2741 {
2742 if (msg[0]!="" && msg[0][<1]==' ') msg[0]=msg[0][0..<2];
2743 SetProp(P_MSGOUT, msg[0]+"#"+msg[1]);
2744 }
2745 else
2746 SetProp(P_MSGOUT, _unparsed_args()||"geht");
2747 write("Ok.\n");
2748 return 1;
2749}
2750
2751static int setmmin(string str)
2752{
2753 SetProp(P_MMSGIN, _unparsed_args()||"erscheint");
2754 write("Ok.\n");
2755 return 1;
2756}
2757
2758static int setmmout(string str)
2759{
2760 SetProp(P_MMSGOUT, _unparsed_args()||"verschwindet");
2761 write("Ok.\n");
2762 return 1;
2763}
2764
2765static int setcmsg(string str)
2766{
2767 SetProp(P_CLONE_MSG, _unparsed_args()||"zaubert etwas aus "
2768 + QueryPossPronoun(MALE,WEM) + " Aermel hervor");
2769 write("Ok.\n");
2770 return 1;
2771}
2772
2773static int setdmsg(string str)
2774{
2775 SetProp(P_DESTRUCT_MSG, _unparsed_args()||"wird von " + name(WER,1)
2776 + " zerstaeubt");
2777 write("Ok.\n");
2778 return 1;
2779}
2780
2781static int set_title(string str)
2782{
2783 string bonus;
2784
2785 if(!IS_SEER(this_object()) && !FAO_HAS_TITLE_GIFT(this_object()) )
2786 {
2787 return 0;
2788 }
2789
2790 bonus = "";
2791 if (!(str=_unparsed_args()))
2792 str = "";
2793 else if( str[0..2]=="\\b," || str[0..2]=="\\b'" )
2794 {
2795 bonus = "\b"; // ein backspace fuer ein (hoch)komma ist ok! :-)
2796 str = str[2..];
2797 }
2798 if(str=="0") // damit der Gildentitel zum Vorschein kommen kann
2799 SetProp(P_TITLE, 0);
2800 else
2801 SetProp(P_TITLE, bonus+str);
2802 write("Ok.\n");
2803 return 1;
2804}
2805
2806static int extra_input(string str, string look)
2807{
2808 if (str=="**")
2809 {
2810 if (look=="")
2811 SetProp(P_EXTRA_LOOK,0);
2812 else
2813 SetProp(P_EXTRA_LOOK,look);
2814 write("Ok.\n");
2815 return 1;
2816 }
2817 input_to("extra_input",INPUT_PROMPT, "]" ,look+str+"\n");
2818 return 1;
2819}
2820
2821static int extralook(mixed str)
2822{
2823 if( str=="?" )
2824 {
2825 write( "Dein Extralook ist : "+QueryProp(P_EXTRA_LOOK) + "\n");
2826 return 1;
2827 }
2828 write("Bitte gib Deinen Extra-Look ein. Beenden mit **:\n");
2829 input_to("extra_input",INPUT_PROMPT,"]","");
2830 return 1;
2831}
2832
2833static void calculate_value()
2834{
2835 int i, carried_value, value;
2836
2837 carried_value=0;
2838 foreach(object ob: deep_inventory(ME)) {
2839 if (!ob->QueryProp(P_AUTOLOADOBJ))
2840 carried_value+=((value=(int)ob->QueryProp(P_VALUE)) > 1000 ? 1000 : value);
2841 }
2842 SetProp(P_CARRIED_VALUE, carried_value);
2843}
2844
2845/* Called by command 'save' */
2846int save_character() {
2847 save_me(1);
2848 write("Ok.\n");
2849 return 1;
2850}
2851
2852void save_me(mixed value_items)
2853{
2854 // Gaeste werden nicht gespeichert
2855 if( getuid()[0..3]=="gast" )
2856 return;
2857
2858 // Autoloader identifizieren und speichern
2859 autoload=([]);
2860 foreach(object ob: deep_inventory(ME)) {
2861 int val = ob->QueryProp( P_AUTOLOADOBJ );
2862 if (val && clonep(ob))
2863 {
2864 string obname=load_name(ob);
2865 if (obname == GELD)
2866 autoload[obname] += val;
2867 else
2868 autoload += ([obname:val]);
2869 }
2870 }
2871 // An noch nicht geclonte Autoloader denken!
2872 autoload += autoload_rest;
2873
2874 // Im Bedarfsfall Wert des Inventory bestimmen
2875 if (value_items)
2876 calculate_value();
2877 else
2878 SetProp(P_CARRIED_VALUE, 0);
2879
2880 // Logout-Zeit speichern
2881 if(query_once_interactive(ME) && !QueryProp(P_INVIS))
2882 Set(P_LAST_LOGOUT,time());
2883
2884 // Funktion zur Bestimmung des Gildenrating aufrufen
2885 string gilde=GUILD_DIR+QueryProp(P_GUILD);
2886 if (find_object(gilde) || file_size(gilde+".c")>-1)
2887 catch(call_other(gilde,"GuildRating",this_object());publish);
2888
2889 // Speichern des Spielers
Zesstra3162c892017-01-30 15:55:22 +01002890 save_object(SAVEPATH+getuid()[0..0]+"/" + getuid());
MG Mud User88f12472016-06-24 23:31:02 +02002891}
2892
2893static varargs void log_autoload( string file, string reason, mixed data, string error )
2894{
2895 if (member(autoload_error,file)!=-1) return;
2896 log_file(SHELLLOG("NO_AUTO_FILE"),sprintf("%s: %s: %s\nreason: cannot %s file\ndata: %O\n%s\n",
2897 ctime()[4..15],capitalize(getuid()),file,reason,data,
2898 (error?"Fehlermeldung: "+error+"\n":"")));
2899 autoload_error+=({file});
2900}
2901
2902// tics, die fuer autoloader reichen sollten:
2903#define SAFE_FOR_AUTOLOADER __MAX_EVAL_COST__/4
2904
2905private void load_auto_object( string file, mixed data )
2906{
2907 object ob;
2908 string error;
2909
2910 if( get_eval_cost() < SAFE_FOR_AUTOLOADER ) return;
2911 m_delete( autoload_rest, file );
2912 autoload_error-=({file});
2913
2914 if ( file == "/obj/money" )
2915 file = "/items/money";
2916 if ( file == "/obj/seercard" )
2917 file = "/items/seercard";
2918
2919 ob = find_object(file);
2920
2921 if (!ob)
2922 {
2923 if (file_size(file+".c")<0&&
2924 file_size(implode(explode(file,"/")[0..<2],"/")+
2925 "/virtual_compiler.c")<0)
2926 {
2927 log_autoload(file,"find",data,0);
2928 return;
2929 }
2930 if (error = catch(load_object(file); publish))
2931 {
2932 log_autoload(file,"load",data,error);
2933 return;
2934 }
2935 }
2936 if ( error = catch(ob = clone_object(file); publish) )
2937 {
2938 log_autoload(file,"clone",data, error);
2939 return;
2940 }
2941
2942 if ( error = catch(ob->SetProp( P_AUTOLOADOBJ, data ); publish) )
2943 {
2944 log_autoload(file,"SetProp",data, error);
2945 ob->remove(1);
2946 if (ob) destruct(ob);
2947 return;
2948 }
2949
2950 if ( error = catch(ob->move( ME, M_NOCHECK );publish) ) {
2951 log_autoload(file,"move",data, error);
2952 ob->remove(1);
2953 if(ob) destruct(ob);
2954 return;
2955 }
2956}
2957
2958static void load_auto_objects( mapping map_ldfied )
2959{
2960 if ( (!mappingp(map_ldfied) || !sizeof(map_ldfied)) && !sizeof(autoload_rest) )
2961 return;
2962
2963 // Mit Netztoten Spielern rechnen manche Autoloader nicht. Also
2964 // das Clonen unterbrechen und in Reconnect() wieder anwerfen.
2965 if ( !interactive() )
2966 return;
2967
2968 // Kleiner Hack: autoload_rest ist eine globale Variable, die beim
2969 // Clonen der einzelnen Autoloader direkt veraendert wird.
2970 // So lange das Mapping noch Eintraege hat, muessen wir noch fehlende
2971 // Autoloader clonen.
2972 if ( !sizeof(autoload_rest) )
2973 autoload_rest = map_ldfied;
2974
2975 // Schon hier einen call_out() zum "Nach"clonen von noch nicht geclonten
2976 // Autoloadern starten, da spaeter u.U. keine Rechenzeit mehr dafuer da ist.
2977 while ( remove_call_out("load_auto_objects") != -1 )
2978 /* do nothing */;
2979
2980 // Mit Parameter '0' aufrufen, da das globale Mapping benutzt wird.
2981 // Verzoegerung 0 in rekursiven Callouts ist bloed, also 1s Delay
2982 call_out( "load_auto_objects", 2, 0 );
2983
2984 // Mit catch() gegen die Evalcost-Falle!
2985 // Mit Absicht das walk_mapping() aus der "alten" Version erhalten und
2986 // nicht durch eine (einfachere) Schleife inkl. get_eval_cost() ersetzt,
2987 // da eine Schleife gegenueber der walk_mapping()-Loesung den Aufbau
2988 // der previous_object()-Kette veraendern wuerde; darauf testen aber
2989 // manche Objekte.
2990 catch( walk_mapping( autoload_rest, #'load_auto_object/*'*/ ) );
2991}
2992
2993/*
2994 * al_to_title: Make the numeric alignment value into a string
2995 */
2996static string al_to_title(int a)
2997{
2998 if (a >= KILL_NEUTRAL_ALIGNMENT * 100)
2999 return "heilig";
3000 if (a > KILL_NEUTRAL_ALIGNMENT * 20)
3001 return "gut";
3002 if (a > KILL_NEUTRAL_ALIGNMENT * 4)
3003 return "nett";
3004 if (a > - KILL_NEUTRAL_ALIGNMENT * 4)
3005 return "neutral";
3006 if (a > - KILL_NEUTRAL_ALIGNMENT * 20)
3007 return "frech";
3008 if (a > - KILL_NEUTRAL_ALIGNMENT * 100)
3009 return "boese";
3010 return "satanisch";
3011}
3012
3013static int toggle_whimpy_dir(string str) {
3014 SetProp(P_WIMPY_DIRECTION,str=_unparsed_args()||str);
3015 if (str)
3016 printf("Ok, Fluchtrichtung %O.\n",str);
3017 else
3018 printf("Ok, bevorzugte Fluchtrichtung deaktiviert.\n");
3019 return 1;
3020}
3021
3022static int toggle_whimpy(string str)
3023{
3024 int i;
3025
3026 if(!str || str=="" || (sscanf(str,"%d",i)<0))
3027 {
3028 write("vorsicht <hp>, 0<=hp<"+QueryProp(P_MAX_HP)+"\n");
3029 return 1;
3030 }
3031 if(i>=QueryProp(P_MAX_HP) || i<0)
3032 {
3033 write("Der Wert ist nicht erlaubt.\n");
3034 return 1;
3035 }
3036 if(!i) write("Prinz Eisenherz-Modus.\n");
3037 else write("Vorsicht-Modus ("+i+")\n");
3038 SetProp(P_WIMPY,i);
3039 return 1;
3040}
3041
3042/** Bestimmt, ob das Spielerobjekt beschattet werden darf.
3043 * Beschatten ist nur fuer Objekte erlaubt, die in /std/player/shadows
3044 * abgelegt sind.
3045 * Ausnahme: Testspieler mit gesetztem P_ALLOWED_SHADOW
3046 * @param[in] obj Objekt, was beschatten moechte.
3047 * @return 0, wenn Beschatten erlaubt, 1 sonst.
3048 */
3049varargs nomask int query_prevent_shadow(object obj)
3050{
3051 string what, allowed_shadow;
3052 int dummy;
3053
3054// if ( Query(P_TESTPLAYER) )
3055// return 0;
3056
3057 if (obj){
3058 what=object_name(obj);
3059 if (what && what != "" &&
3060 sscanf(what,"/std/player/shadows/%s#%d",what,dummy)==2)
3061 return 0;
3062
3063 // Einem Testspieler kann man P_ALLOWED_SHADOW auf einen zu testenden
3064 // Shadow setzen.
3065 if ( Query(P_TESTPLAYER) &&
3066 stringp(what) &&
3067 stringp(allowed_shadow=Query(P_ALLOWED_SHADOW)) &&
3068 strstr(what, allowed_shadow)==0)
3069 return 0;
3070 }
3071 return 1;
3072}
3073
3074static int uhrzeit()
3075{
3076 write(dtime(time()+QueryProp(P_TIMEZONE)*3600)+".\n");
3077 return 1;
3078}
3079
3080protected string SetDefaultHome(string str)
3081{
3082 return default_home=str;
3083}
3084
3085string QueryDefaultHome()
3086{
3087 return default_home;
3088}
3089
3090protected string SetDefaultPrayRoom(string str)
3091{
3092 if(hc_play>1)
3093 {
3094 default_pray_room="/room/nirvana";
3095 }
3096 else
3097 default_pray_room=str;
3098
3099 return default_pray_room;
3100}
3101
3102string QueryPrayRoom()
3103{
3104 if(hc_play>1)
3105 {
3106 return "/room/nirvana";
3107 }
3108 string room = QueryProp(P_PRAY_ROOM);
3109 if (stringp(room))
3110 return room;
3111 else if (default_pray_room)
3112 return default_pray_room;
3113 // hoffentlich ist das wenigstens gesetzt.
3114 return default_home;
3115}
3116
3117void _restart_beat()
3118{
3119 tell_object(ME,
3120 "Der GameDriver teilt Dir mit: Dein Herzschlag hat wieder eingesetzt.\n");
3121 set_heart_beat(1);
3122}
3123
3124static int weg(string str)
3125{
3126 if (!(str=_unparsed_args()))
3127 {
3128 printf("Du bist nicht%s als abwesend gekennzeichnet.\n",
3129 QueryProp(P_AWAY) ? " mehr" : "");
3130 SetProp(P_AWAY, 0);
3131 return 1;
3132 }
3133 write("Du bist jetzt als abwesend gekennzeichnet.\n");
3134 SetProp(P_AWAY, str);
3135 return 1;
3136}
3137
3138/* Ein Befehl zum anschauen der Wegmeldung anderer Spieler */
3139static int wegmeldung(string player)
3140{
3141
3142 object player_ob;
3143 string weg;
3144
3145 if ( !player || player=="" ||
3146 player==lowerstring(this_player()->QueryProp(P_NAME)))
3147 {
3148 weg=this_player()->QueryProp(P_AWAY);
3149 write ("Du bist "+(weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
3150 if (weg)
3151 write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
3152 return 1;
3153 }
3154
3155 // Welcher Spieler ist gemeint?
3156 player_ob=find_player(player);
3157
3158 // Spieler nicht da oder Invis und Anfrager is kein Magier
3159 if (!player_ob ||
3160 (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
3161 {
3162 write(capitalize(player)+" ist gerade nicht im Spiel.\n");
3163 return 1;
3164 }
3165
3166 weg=player_ob->QueryProp(P_AWAY);
3167
3168 // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
3169 write (player_ob->QueryProp(P_NAME)+" ist "+
3170 (weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
3171
3172 if (weg)
3173 write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
3174
3175 return 1;
3176}
3177
3178static string timediff(int time)
3179{
3180 string ret;
3181
3182 ret="";
3183 if(time>=86400) {
3184 ret+=time/86400+"d ";
3185 time%=86400;
3186 }
3187 if(time<36000) ret+="0";
3188 ret+=time/3600+":";
3189 time%=3600;
3190 if(time<600) ret+="0";
3191 ret+=time/60+":";
3192 time%=60;
3193 if(time<10) ret+="0";
3194 ret+=time+"";
3195 return ret;
3196}
3197
3198
3199/* Ein Befehl zum anschauen der Idlezeit anderer Spieler */
3200static int idlezeit(string player)
3201{
3202
3203 object player_ob;
3204 int idle;
3205
3206 if ( !player || player=="" ||
3207 player==lowerstring(this_player()->QueryProp(P_NAME)))
3208 {
3209 write ("Du bist selber natuerlich gerade nicht idle.\n");
3210 return 1;
3211 }
3212
3213 // Welcher Spieler ist gemeint?
3214 player_ob=find_player(player);
3215
3216 // Spieler nicht da oder Invis und Anfrager is kein Magier
3217 if (!player_ob ||
3218 (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
3219 {
3220 write(capitalize(player)+" ist gerade nicht im Spiel.\n");
3221 return 1;
3222 }
3223
3224 idle=query_idle(player_ob);
3225
3226 // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
3227 write (player_ob->QueryProp(P_NAME)+" ist "+
3228 (idle>=60?timediff(idle):"nicht")+" passiv.\n");
3229
3230 return 1;
3231}
3232
3233
3234/** Belebt einen netztoten Spieler wieder.
3235 * Reaktiviert Heartbeats, bewegt den Spieler zurueck an den Ort, der eim
3236 * Einschlafen zum Aufwachen ermittelt wurde (im einfachsten Fall der Raum,
3237 * wo man eingeschlafen ist).
3238 */
3239static void ndead_revive()
3240{
3241 string fname;
3242 int ret;
3243
3244 set_heart_beat(1);
3245 ndead_next_check = NETDEAD_CHECK_TIME;
3246 ndead_currently = 0;
3247 ndead_lasttime = 0;
3248
3249 if ( !objectp(ndead_location) &&
3250 stringp(ndead_l_filename) && sizeof(ndead_l_filename)) {
3251
3252 if ( member( ndead_l_filename, '#' ) == -1 ){
3253 catch(load_object( ndead_l_filename); publish);
3254 ndead_location = find_object(ndead_l_filename);
3255 }
3256 else {
3257 ndead_location = find_object(ndead_l_filename);
3258 if ( !ndead_location && env_ndead_info ){
3259 fname = explode( ndead_l_filename, "#" )[0];
3260 catch(ndead_location =
3261 (load_object(fname)->SetProp(P_NETDEAD_INFO, env_ndead_info));
3262 publish);
3263 if ( !objectp(ndead_location) ){
3264 catch(load_object( ndead_location);publish);
3265 ndead_location = find_object(ndead_location);
3266 }
3267 }
3268 }
3269 }
3270
3271 if ( !objectp(ndead_location)
3272 || catch(ret = move( ndead_location, M_GO|M_SILENT );publish)
3273 || ret != 1 ) {
3274 move( "gilden/abenteurer", M_GO|M_SILENT );
3275 ndead_location = environment();
3276 }
3277
3278 // ndead_location=0;
3279 ndead_l_filename = 0;
3280 env_ndead_info = 0;
3281}
3282
3283/** Bewegt einen netztoten Spieler in den Netztotenraum
3284 * Gerufen von heartbeat().
3285 * Zerstoert Gaeste, verlaesst ggf. das Team, ermittelt, ob der Spieler beim
3286 * Aufwachen in das alte Environment bewegt werden soll oder in einen anderen
3287 * Raum, hierzu wird im Environment P_NETDEAD_INFO abgefragt.
3288 * Deaktiviert die Kommandos per disable_commands().
3289 */
3290static void ndead_move_me() {
3291 object team;
3292 mixed amem;
3293
3294 set_heart_beat(0);
3295 stop_heart_beats();
3296 if (QueryGuest()) {
3297 quit();
3298 if (ME)
3299 remove();
3300 if (ME)
3301 destruct(ME);
3302 return;
3303 }
3304 ndead_next_check=NETDEAD_CHECK_TIME;
3305 ndead_currently=1;
3306 ndead_lasttime=0;
3307 ndead_location=environment();
3308 if (objectp(ndead_location))
3309 ndead_l_filename=object_name(ndead_location);
3310
3311 if (objectp(environment())
3312 && sizeof(explode(object_name(environment()),"#")) > 1)
3313 env_ndead_info=environment()->QueryProp(P_NETDEAD_INFO);
3314 else
3315 env_ndead_info=0;
3316
3317 if ( objectp(team=Query(P_TEAM)) )
3318 // Der Test auf assoziierte Teammitglieder (== FolgeNPCs)
3319 // verhindert, dass Spieler nach "schlafe ein" aus dem
3320 // Team ausgetragen werden. -- 29.01.2002 Tiamak
3321 // && !objectp(amem=Query(P_TEAM_ASSOC_MEMBERS))
3322 // && !(pointerp(amem) && sizeof(amem)))
3323 team->RemoveMember(ME);
3324
3325 disable_commands();
3326 move(NETDEAD_ROOM,M_GO|M_NO_ATTACK|M_NOCHECK,"ins Reich der Netztoten");
3327}
3328
3329/** Ist dieser Character ein Gast?
3330 * @return 1, wenn Gast, 0 sonst.
3331 */
3332int QueryGuest()
3333{
3334 string dummy;
3335 return sscanf(getuid(),"gast%d",dummy);
3336}
3337
3338/** Spielerkommando 'schlafe ein'.
3339 * Ruft remove_interactive() bei Spielern, bei Magiern wird quit() gerufen,
3340 * um das Magierobjekt zu zerstoeren.
3341 * @sa quit()
3342 */
3343int disconnect(string str)
3344{
3345 string verb;
3346 string desc = break_string(
3347 "\"schlafe ein\" beendet Deine Verbindung mit "MUDNAME". Du behaeltst "
3348 "Deine Sachen.\nFalls "MUDNAME" jedoch abstuerzt oder neu gestartet "
3349 "wird, waehrend Du weg bist, verlierst Du die meisten allerdings "
3350 "(genauso, als wenn Du Deine Verbindung mit \"ende\" beendet haettest). "
3351 "In diesem Fall bekommst Du dann eine kleine Entschaedigung."
3352 ,78,0,BS_LEAVE_MY_LFS);
3353
3354 verb=query_verb();
3355 if (!verb)
3356 verb="AUTOCALL";
3357 if (verb[0..5]=="schlaf" && str!="ein")
3358 {
3359 notify_fail(desc);
3360 return 0;
3361 }
3362 if (IS_LEARNER(this_object()))
3363 return quit();
3364
3365 tell_object(this_object(), desc);
3366
3367 if (clonep(environment()) && !environment()->QueryProp(P_NETDEAD_INFO))
3368 tell_object(this_object(),break_string(
3369 "\nACHTUNG: Wenn Du hier laengere Zeit schlaefst, "
3370 "kommst Du vermutlich nicht an diesen Ort zurueck!",78));
3371
3372 say(capitalize(name(WER))+" hat gerade die Verbindung zu "MUDNAME" gekappt.\n");
3373 remove_interactive(ME);
3374 call_out(#'clear_tell_history,4);
3375 return 1;
3376}
3377
3378static int finger (string str)
3379{
3380 string ret;
3381 mixed xname;
3382
3383 if (!str || str==""
3384 || sizeof(explode(str," ")-({"-n","-p","-s","-v","-a"}))>1)
3385 {
3386 write("finger <spielername> oder finger <spielername@mudname>\n"+
3387 "Bitte nur den reinen Spielernamen verwenden, keine Namensvorsaetze oder Titel\n");
3388 return 1;
3389 }
3390 xname=explode(str,"@");
3391 if(sizeof(xname)==2)
3392 {
3393 if (xname[0]=="-n " || xname[0]=="-p " || xname[0]=="-s ") {
3394 write("finger <spielername>@<mudname> - der Spielername fehlt.\n");
3395 return 1;
3396 }
3397 if (ret=INETD->_send_udp(xname[1],([
3398 REQUEST: "finger",
3399 SENDER: getuid(ME),
3400 DATA: (explode(xname[0]," ")-({"-n","-p","-s"}))[0]
3401 ]), 1))
3402 write(ret);
3403 else
3404 write("Anfrage abgeschickt.\n");
3405 return 1;
3406 }
3407 "/p/daemon/finger"->finger_single(str,1);
3408 return 1;
3409}
3410
3411string lalign(string str, int wid)
3412{
3413 return (str+" "+
3414 " ")[0..wid-1];
3415}
3416
3417#define MUDS_BAR "\
3418-------------------------------------------------------------------------------"
3419
3420private void format(mixed mud, mixed hosts, string output)
3421{
3422 output += lalign(hosts[mud][HOST_NAME], 20) + " " +
3423 (hosts[mud][HOST_STATUS] ?
3424 hosts[mud][HOST_STATUS] > 0 ?
3425 "UP " + ctime(hosts[mud][HOST_STATUS])[4..15] :
3426 "DOWN " + ctime(-hosts[mud][HOST_STATUS])[4..15]
3427 : "UNKNOWN Never accessed.") + "\n";
3428}
3429
3430static int muds() {
3431 mapping hosts;
3432 int i;
3433 mixed muds, output;
3434
3435 output = lalign("Mudname", 20) + " Status Last access";
3436 output += "\n" + MUDS_BAR[0..sizeof(output)] + "\n";
3437 muds = sort_array(m_indices(hosts = INETD->query("hosts")),#'>);
3438 map(muds, #'format, hosts, &output);
3439 More(output);
3440 return 1;
3441}
3442
3443// **** local property methods
3444static int _set_level(int i)
3445{
3446 if (!intp(i)) return -1;
3447 if (i<1) return -1;
3448 Set(P_LEVEL, i);
3449 GMCP_Char( ([P_LEVEL: i]) );
3450 return i;
3451}
3452
3453static int _set_invis(int a)
3454{
3455 return Set(P_INVIS, intp(a) ? a : !Query(P_INVIS));
3456}
3457
3458/* sets the terminal type */
3459/* note: support vt100 (b/w), ansi (color), dumb (none) */
3460static string _set_tty(string str) {
3461 if(str != "dumb" && str != "vt100" && str != "ansi")
3462 return Query(P_TTY);
3463 return Set(P_TTY, str);
3464}
3465
3466static int stty(string str)
3467{
3468 if(str!="dumb"&&str!="vt100"&&str!="ansi"&&str!="reset")
3469 {
3470 write("Kommando: stty dumb|vt100|ansi oder reset\n");
3471 }
3472 if(str == "reset") {
3473 printf("Dieser Text sollte lesbar sein!\n");
3474 return 1;
3475 }
3476
3477 write("TTY steht jetzt auf "+SetProp(P_TTY,str)+".\n");
3478 if(str == "ansi" || str == "vt100") {
3479 printf("Terminal Test:\n");
3480 printf("VT100: fett unterstrichen "+
3481 "blinkend invers\n");
3482 if(str == "ansi") {
3483 printf("ANSI Farben und VT100 Attribute:\n");
3484 foreach(int fg: 30 .. 37) {
3485 foreach(int bg: 40 .. 47) {
3486 printf("[%d;%dm@", fg, bg);
3487 printf("[%d;%dm@", fg, bg);
3488 printf("[%d;%dm@", fg, bg);
3489 printf("[%d;%dm@", fg, bg);
3490 }
3491 printf("\n");
3492 }
3493 printf("Sollte dieser Text hier nicht richtig lesbar\nsein,"+
3494 "benutze das Kommando stty reset!\n");
3495 }
3496
3497 }
3498 return 1;
3499}
3500
3501int set_ascii_art(string str)
3502{
3503 if (str!="ein"&&str!="aus")
3504 {
3505 printf("Du moechtest 'Grafik' "+(QueryProp(P_NO_ASCII_ART)?"NICHT ":"")+
3506 "sehen.\n");
3507 }
3508
3509 if (str=="ein") {
3510 SetProp(P_NO_ASCII_ART, 0);
3511 printf("Zukuenftig moechtest Du 'Grafik' sehen.\n");
3512 }
3513
3514 if (str=="aus") {
3515 SetProp(P_NO_ASCII_ART, 1);
3516 printf("Zukuenftig moechtest Du KEINE 'Grafik' mehr sehen.\n");
3517 }
3518
3519
3520 return 1;
3521}
3522
3523int _set_shell_version(int arg)
3524{
3525 if (!intp(arg))
3526 return -1;
3527 Set(P_SHELL_VERSION,({QueryProp(P_RACE),arg}));
3528 return 1;
3529}
3530
3531int _query_shell_version()
3532{ mixed sv;
3533
3534 if (!(sv=Query(P_SHELL_VERSION)) || !pointerp(sv) || sizeof(sv)!=2 ||
3535 sv[0]!=QueryProp(P_RACE) || !intp(sv[1]))
3536 return 0;
3537 return sv[1];
3538}
3539
3540// XxXxXxXxXx
3541
3542int more(string str)
3543{
3544 if(!str)
3545 {
3546 notify_fail("Usage: more <file>\n");
3547 return 0;
3548 }
3549 if (file_size(str) <= 0) {
3550 notify_fail(str+": No such file\n");
3551 return 0;
3552 }
3553 More(str, 1);
3554 return 1;
3555}
3556
3557static int set_visualbell(string str)
3558{
3559 if(!str)
3560 {
3561 write("Derzeitige Einstellung fuer Tonausgabe: "+
3562 (QueryProp(P_VISUALBELL)?"AUS":"EIN")+".\n");
3563 return 1;
3564 }
3565 if (str=="ein")
3566 {
3567 if(!QueryProp(P_VISUALBELL))
3568 write("Die Tonausgabe stand schon auf EIN.\n");
3569 else
3570 {
3571 SetProp(P_VISUALBELL,0);
3572 write("OK, Tonausgabe auf EIN gestellt.\n");
3573 }
3574 }
3575 else
3576 if (str=="aus")
3577 {
3578 if(QueryProp(P_VISUALBELL))
3579 write("Die Tonausgabe stand schon auf AUS.\n");
3580 else
3581 {
3582 SetProp(P_VISUALBELL,1);
3583 write("OK, Tonausgabe auf AUS gestellt.\n");
3584 }
3585 }
3586 else
3587 write("Syntax: ton [ein|aus]\n");
3588 return 1;
3589}
3590
3591static int set_screensize(string str)
3592{
3593 int size;
3594
3595 if (str && (str[0..2] == "abs" || str[0..2]=="rel")) {
3596 size = QueryProp(P_MORE_FLAGS);
3597 if (str[0..2] == "abs") {
3598 size |= E_ABS;
3599 write("Es wird beim Prompt die Zeilenzahl des Textes angegeben.\n");
3600 }
3601 else {
3602 size &= ~E_ABS;
3603 write("Es wird beim Prompt der prozentuale Anteil des Textes angegeben.\n");
3604 }
3605 SetProp(P_MORE_FLAGS, size);
3606 return 1;
3607 }
3608
3609 if ( str && (str=="auto" || sscanf( str, "auto %d", size )) ){
3610 if ( size > 0 ){
3611 write("Ungueltiger Wert! "
3612 "In Verbindung mit 'auto' sind nur negative Werte erlaubt.\n");
3613 return 1;
3614 }
3615
3616 SetProp( P_SCREENSIZE, size-1 );
3617
3618 write("Ok, Deine Zeilenzahl wird nun automatisch ermittelt (derzeit "+
3619 QueryProp(P_SCREENSIZE)+").\n"+
3620 break_string("Bitte beachte, dass dies nur einwandfrei "
3621 "funktioniert, wenn Dein Client Telnetnegotiations "
3622 "unterstuetzt (siehe auch \"hilfe telnegs\").") );
3623 return 1;
3624 }
3625
3626 if ( !str || str=="" || !sscanf( str, "%d", size ) || size < 0 || size > 100){
3627 write(break_string(
3628 sprintf("Mit dem Befehl 'zeilen <groesse>' kannst Du einstellen, "
3629 "wieviele Zeilen bei mehrseitigen Texten auf einmal ausgegeben "
3630 "werden. Die angegebene Groesse muss zwischen 0 und 100 liegen. "
3631 "Bei Groesse 0 wird einfach alles ausgegeben (ohne Pause). Mit "
3632 "der Einstellung 'auto' wird die Groesse automatisch ueber "
3633 "die Telnetnegotiations ermittelt (siehe auch 'hilfe telnegs'). "
3634 "Um nach einer Seite Text noch etwas Platz zu haben, kann man z.B. "
3635 "'zeilen auto -3' einstellen.\n"
3636 "Die Voreinstellung ist 20 Zeilen.\n"
3637 "Mit 'zeilen abs[olut]' und 'zeilen rel[ativ]' kannst Du fest"
3638 "legen, ob im Prompt bei langen Texten die aktuelle Zeilennummer "
3639 "oder eine prozentuale Angabe ausgegeben wird.\n"
3640 "Deine aktuelle Einstellung ist %d%s Zeilen (%s).",
3641 QueryProp(P_SCREENSIZE),
3642 Query(P_SCREENSIZE) < 0 ? " 'automatische'" : "",
3643 QueryProp(P_MORE_FLAGS) & E_ABS ? "absolut" : "relativ"),78,0,1));
3644 return 1;
3645 }
3646
3647 SetProp( P_SCREENSIZE, size );
3648
3649 printf( "Okay, Deine Zeilenzahl steht nun auf %d.\n", size );
3650 return 1;
3651}
3652
3653static int _query_screensize()
3654{
3655 int sz,rows;
3656
3657 if ( (sz=Query(P_SCREENSIZE)) >= 0 )
3658 return sz;
3659
3660 if ( !rows=QueryProp(P_TTY_ROWS) )
3661 return 0;
3662
3663 return (rows+=sz) >= 5 ? rows : 5;
3664}
3665
3666static int presay(string str)
3667{
3668 if (!str=_unparsed_args())
3669 write("Dein Presay ist jetzt geloescht.\n");
3670 else
3671 printf("Dein Presay lautet jetzt: \"%s\".\n",str=capitalize(str));
3672 SetProp(P_PRESAY,str);
3673 return 1;
3674}
3675
3676static int sethands(string str)
3677{
3678 mixed *hands;
3679
3680 if (!(str=_unparsed_args()))
3681 {
3682 write("sethands <message>\n");
3683 return 1;
3684 }
3685 if (str=="0")
3686 hands=RaceDefault(P_HANDS);
3687 if (!hands || !pointerp(hands))
3688 hands=Query(P_HANDS);
3689 hands[0]=" "+str;
3690 Set(P_HANDS,hands);
3691 write("Ok.\n");
3692 return 1;
3693}
3694
3695static int inform(string str)
3696{
3697 switch (str) {
3698 case "on":
3699 case "ein":
3700 case "an":
3701 if (Query(P_INFORMME))
3702 write("Das hattest Du schon so eingestellt.\n");
3703 else
3704 {
3705 write("Kuenftig wirst Du informiert, wenn jemand das "MUDNAME" verlaesst/betritt.\n");
3706 Set(P_INFORMME,1);
3707 }
3708 return 1;
3709 case "aus":
3710 case "off":
3711 if (!Query(P_INFORMME))
3712 write("Das hattest Du schon so eingestellt.\n");
3713 else
3714 {
3715 write("Ok.\n");
3716 Set(P_INFORMME,0);
3717 }
3718 return 1;
3719 case 0:
3720 write("Inform-Mode ist "+(Query(P_INFORMME)?"an":"aus")+"geschaltet.\n");
3721 return 1;
3722 }
3723 write("inform an oder inform aus, bitte.\n");
3724 return 1;
3725}
3726
3727void delayed_write(mixed *what)
3728{
3729 if (!pointerp(what)||!sizeof(what)||!pointerp(what[0]))
3730 return;
3731 tell_object(ME,what[0][0]);
3732 if (sizeof(what)>1&&sizeof(what[0])>1)
3733 call_out("delayed_write",what[0][1],what[1..]);
3734}
3735
3736void notify_player_change(string who, int rein, int invis)
3737{
3738 string *list,name;
3739 mixed mlist;
3740
3741 if (invis) name="("+who+")";
3742 else name=who;
3743
3744 if (Query(P_INFORMME))
3745 {
3746 if (rein)
3747 tell_object(ME,name+" ist gerade ins "MUDNAME" gekommen.\n");
3748 else
3749 tell_object(ME,name+" hat gerade das "MUDNAME" verlassen.\n");
3750 }
3751
3752 if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
3753
3754 if(pointerp(list=Query(P_WAITFOR)) && sizeof(list) && member(list,who)!=-1)
3755 {
3756 if (!QueryProp(P_VISUALBELL))
3757 name+=sprintf("%c",7); // Char fuer Pieps an den String anhaengen.
3758 // Moechte der Spieler keine ASCII-Grafik sehen, wird diese Meldung ohne
3759 // Leerzeichen formatiert, so dass sie von Screenreadern vorgelesen wird.
3760 // Anderenfalls wuerde sie einzeln buchstabiert.
3761 if ( QueryProp(P_NO_ASCII_ART) )
3762 {
3763 delayed_write( ({ ({ sprintf("%s IST JETZT %sDA !!!\n",
3764 name, (rein?"":"NICHT MEHR ")) }) }) );
3765 }
3766 else
3767 {
3768 delayed_write( ({ ({ sprintf("%s I S T J E T Z T %sD A !!!\n",
3769 name, (rein?"":"N I C H T M E H R ")) }) }) );
3770 }
3771 }
3772
3773 if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
3774 (mappingp(mlist)) && (mlist[who]))
3775 Show_WaitFor_Reason(who,invis);
3776}
3777
3778static int erwarte(string str)
3779{
3780 string *list,*str1;
3781 mixed mlist;
3782 int i;
3783
3784 if (!mappingp(mlist=QueryProp(P_WAITFOR_REASON)))
3785 mlist=([]);
3786 if (!pointerp(list=Query(P_WAITFOR)))
3787 list=({});
3788
3789 if (!str || str=="-u")
3790 {
3791 if(Query(P_WAITFOR_FLAGS)&0x01)
3792 write("Du hast 'erwarte' temporaer deaktiviert.\n");
3793 write("Du erwartest jetzt");
3794 if (!sizeof(list))
3795 write(" niemanden mehr.\n");
3796 else
3797 {
3798 write(":\n");
3799 if (!str) list=sort_array(list,#'>);
3800 More(break_string(CountUp(list),78));
3801 }
3802 return 1;
3803 }
3804 if(str=="aus"){
3805 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)|0x01);
3806 write("Erwarte ist jetzt deaktiviert.\n");
3807 return 1;
3808 }
3809 if(str=="an" || str=="ein"){
3810 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)&0xFE);
3811 write("Erwarte ist jetzt aktiv.\n");
3812 return 1;
3813 }
3814
3815 str1=explode(_unparsed_args()||""," ");
3816 if (sizeof(str1)==1)
3817 {
3818 if (str1[0]!="wegen")
3819 {
3820 str=capitalize(lower_case(str));
3821 if (member(list,str)!=-1)
3822 {
3823 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,str));
3824 list-=({str});
3825 write(str+" aus der Liste entfernt.\n");
3826 } else
3827 {
3828 if (sizeof(list)>1000)
3829 {
3830 write("Du erwartest schon genuegend!\n");
3831 return 1;
3832 }
3833 list+=({str});
3834 write(str+" an die Liste angehaengt.\n");
3835 }
3836 Set(P_WAITFOR,list);
3837 }
3838 else
3839 {
3840 if (sizeof(mlist) && sizeof(list=m_indices(mlist)))
3841 {
3842 write("Du erwartest aus einem bestimmten Grund:\n");
3843 write(break_string(CountUp(sort_array(list,#'>))+".",78));
3844 }
3845 else write("Du erwartest niemanden aus einem bestimmten Grund.\n");
3846 }
3847 return 1;
3848 }
3849 notify_fail("Falsche Syntax, siehe 'hilfe erwarte'!\n");
3850 if (str1[1]!="wegen") return 0;
3851 if (sizeof(str1)==2)
3852 Show_WaitFor_Reason(capitalize(lower_case(str1[0])),0);
3853 else {
3854 string s=capitalize(lower_case(str1[0]));
3855 if (sizeof(str1)==3 && (str1[2]=="nichts" || str1[2]=="loeschen"))
3856 if (!mlist[s])
3857 write("Du hast "+s+" aus keinem bestimmten Grund erwartet!\n");
3858 else
3859 {
3860 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,s));
3861 write("Du erwartest "+s+" aus keinem bestimmten Grund mehr!\n");
3862 }
3863 else
3864 {
3865 if (IS_ARCH(ME)) i=80; else if (IS_LEARNER(ME)) i=40;
3866 else if (IS_SEER(ME)) i=20; else i=10;
3867 if (!mlist[s] && sizeof(mlist)>=i)
3868 write("Sorry, aber Du erwartest schon genuegend Leute!\n");
3869 else
3870 {
3871 SetProp(P_WAITFOR_REASON,mlist+([s:implode(str1[2..]," ")]));
3872 Show_WaitFor_Reason(s,0);
3873 }
3874 }
3875 }
3876 return 1;
3877}
3878
3879static int uhrmeldung(string str)
3880{
3881 if (!(str=_unparsed_args()))
3882 {
3883 str=QueryProp(P_CLOCKMSG);
3884 if (!str)
3885 {
3886 write("Du hast die Standard-Uhrmeldung.\n");
3887 return 1;
3888 }
3889 if( !stringp(str) ) str = sprintf("%O\n",str);
3890 printf("Deine Uhrmeldung ist:\n%s\n",str[0..<2]);
3891 return 1;
3892 }
3893 if (str=="0")
3894 {
3895 SetProp(P_CLOCKMSG,0);
3896 write("Ok, Du hast jetzt wieder die Standard-Meldung.\n");
3897 return 1;
3898 }
3899 if (sizeof(explode(str,"%d"))>2)
3900 {
3901 write("Fehler, es darf nur ein %d in der Meldung vorkommen.\n");
3902 return 1;
3903 }
3904 /* Mehrere %-Parameter verursachen das Abschalten der Uhr zur vollen Stunde.
3905 */
3906 if (sizeof(explode(str,"%"))>2)
3907 {
3908 write("Fehler: Zuviele %-Parameter in der Meldung.\n");
3909 return 1;
3910 }
3911 /* Nur ein %-Parameter, aber der falsche: nicht sinnvoll. */
3912 else
3913 {
3914 int i = strstr(str,"%",0);
3915 if ( i>-1 && ( i==sizeof(str)-1 || str[i+1]!='d'))
3916 {
3917 write("Fehler: Falscher %-Parameter in der Meldung.\n");
3918 return 1;
3919 }
3920 }
3921 str+="\n";
3922 SetProp(P_CLOCKMSG,str);
3923 write("Ok.\n");
3924 return 1;
3925}
3926
3927static int zeitzone(string str)
3928{
3929 int zt;
3930 if(!str || str==""){
3931 if(!(zt=QueryProp(P_TIMEZONE)))
3932 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
3933 "eingestellt.\n");
3934 else if(zt>0)
3935 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
3936 "eingestellt.\n",zt);
3937 else
3938 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
3939 "Berlin eingestellt.\n",-zt);
3940 return 1;
3941 }
3942 if(sscanf(str,"utc %d",zt)==1) zt=(zt-1)%24;
3943 else zt=to_int(str)%24;
3944
3945 SetProp(P_TIMEZONE,zt);
3946
3947 if(!zt)
3948 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
3949 "eingestellt.\n");
3950 else if(zt>0)
3951 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
3952 "eingestellt.\n",zt);
3953 else
3954 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
3955 "Berlin eingestellt.\n",-zt);
3956 return 1;
3957}
3958
3959static int emailanzeige(string str){
3960 notify_fail("Syntax: emailanzeige [alle|freunde|niemand]\n");
3961 if(!str || str==""){
3962 if(!(str=QueryProp(P_SHOWEMAIL)))str="Niemandem";
3963 else if(str=="alle")str="allen";
3964 else if(str=="freunde")str="Deinen Freunden";
3965 else if(str=="niemand")str="niemandem";
3966 else{
3967 SetProp(P_SHOWEMAIL,0);
3968 str="Niemandem";
3969 }
3970 write("Deine Email wird "+str+" angezeigt.\n");
3971 return 1;
3972 }
3973 else if(member(({"alle","freunde","niemand"}),str)==-1)return 0;
3974
3975 SetProp(P_SHOWEMAIL,str);
3976
3977 if(str=="alle")str="allen";
3978 else if(str=="freunde")str="Deinen Freunden";
3979 else str="niemandem";
3980 write("Deine Email wird "+str+" angezeigt.\n");
3981 return 1;
3982}
3983
3984static int zaubertraenke()
3985{
3986 More("/room/orakel"->TipListe());
3987 return 1;
3988}
3989
3990varargs static int angriffsmeldung(string arg) {
3991 if (arg=="ein" || arg=="an")
3992 SetProp(P_SHOW_ATTACK_MSG,1);
3993 else if (arg=="aus")
3994 SetProp(P_SHOW_ATTACK_MSG,0);
3995 if (QueryProp(P_SHOW_ATTACK_MSG))
3996 write("Du siehst saemtliche Angriffsmeldungen von Dir.\n");
3997 else
3998 write("Du siehst nur neue Angriffsmeldungen von Dir.\n");
3999 return 1;
4000}
4001
4002static mixed _query_localcmds()
4003{
4004 return ({({"zeilen","set_screensize",0,0}),
4005 ({"email","set_email",0,0}),
4006 ({"url","set_homepage",0,0}),
4007 ({"icq","set_icq",0,0}),
4008 ({"messenger", "set_messenger", 0, 0}),
4009 ({"ort","set_location",0,0}),
4010 ({"punkte","short_score",0,0}),
4011 ({"score","short_score",0,0}),
4012 ({"info","score",0,0}),
4013 ({"kurzinfo","very_short_score",0,0}),
4014 ({"quit","new_quit",0,0}),
4015 ({"ende","new_quit",0,0}),
4016 ({"disconnect","disconnect",0,0}),
4017 ({"schlaf","disconnect",1,0}),
4018 ({"speichern","save_character",0,0}),
4019 ({"save","save_character",0,0}),
4020 ({"toete","kill",0,0}),
4021 ({"angriffsmeldung","angriffsmeldung",0,0}),
4022 ({"passw","change_password",1,0}),
4023 ({"hilfe","help",1,0}),
4024 ({"selbstloeschung","self_delete",0,0}),
4025 ({"spielpause","spielpause",0,0}),
4026 ({"spieldauer","spieldauer",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004027 ({"idee","ReportError",0,0}),
4028 ({"typo","ReportError",0,0}),
4029 ({"bug","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004030 ({"fehler","fehlerhilfe",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004031 ({"md","ReportError",0,0}),
4032 ({"detail","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004033 ({"vorsicht","toggle_whimpy",0,0}),
4034 ({"stop","stop",0,0}),
4035 ({"kwho","kwho",0,0}),
4036 ({"kwer","kwho",0,0}),
4037 ({"kkwer","kkwho",0,0}),
4038 ({"kkwho","kkwho",0,0}),
4039 ({"who","who",0,0}),
4040 ({"wer","who",0,0}),
4041 ({"zeit","uhrzeit",0,0}),
4042 ({"uhrzeit","uhrzeit",0,0}),
4043 ({"weg","weg",0,0}),
4044 ({"wegmeldung", "wegmeldung", 0, 0}),
4045 ({"idlezeit", "idlezeit", 0, 0}),
4046 ({"finger","finger",0,0}),
4047 ({"muds","muds",0,0}),
4048 ({"emote","emote",0,0}),
4049 ({":","emote",1,0}),
4050 ({";","emote",1,0}),
4051 ({"remote","remote",0,SEER_LVL}),
4052 ({"r:","remote",1,0}),
4053 ({"r;","gremote",1,0}),
4054 ({"titel","set_title",0,0}),
4055 ({"review","review",0,SEER_LVL}),
4056 ({"setmin","setmin",0,SEER_LVL}),
4057 ({"setmout","setmout",0,SEER_LVL}),
4058 ({"setmmin","setmmin",0,SEER_LVL}),
4059 ({"setmmout","setmmout",0,SEER_LVL}),
4060 ({"sethands","sethands",0,SEER_LVL}),
4061 ({"presay","presay",0,SEER_LVL}),
4062 ({"extralook","extralook",0,SEER_LVL}),
4063 ({"fluchtrichtung","toggle_whimpy_dir",0,SEER_LVL}),
4064 ({"inform","inform",0,0}),
4065 ({"erwarte","erwarte",0,0}),
4066 ({"stty","stty",0,0}),
4067 ({"grafik", "set_ascii_art", 0, 0}),
4068 ({"uhrmeldung","uhrmeldung",0,0}),
4069 ({"zeitzone","zeitzone",0,0}),
4070 ({"behalte","behalte",0,0}),
4071 ({"zweitiemarkierung","zweitiemarkierung",0,0}),
4072 ({"emailanzeige","emailanzeige",0,0}),
4073 ({"topliste","topliste",0,0}),
4074 ({"ton","set_visualbell",0,0}),
4075 ({"telnegs","show_telnegs",0,0}),
4076 ({"spotte", "spotte", 0, 0}),
4077 ({"reise","reise",0,0}),
4078 ({"zaubertraenke","zaubertraenke",0,0}),
4079 ({"telnet","telnet_cmd",0,0}),
4080 })+
4081 command::_query_localcmds()+
4082 viewcmd::_query_localcmds()+
4083 comm::_query_localcmds()+
4084 skills::_query_localcmds()+
4085 description::_query_localcmds();
4086}
4087
4088static int _check_keep(object ob)
4089{
4090 return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
4091}
4092
4093static mixed _set_testplayer(mixed arg) {
4094 mixed res;
4095 object setob;
4096
4097 setob=this_player();
4098 if (!objectp(setob) || !query_once_interactive(setob))
4099 setob=this_interactive();
4100 if (!objectp(setob))
4101 setob=previous_object();
4102 if (setob && !IS_DEPUTY(setob)) {
4103 arg=geteuid(setob);
4104 if (!arg || arg=="NOBODY")
4105 arg=getuid(setob);
4106 arg=capitalize(arg);
4107 }
4108 res=Set(P_TESTPLAYER,arg);
4109 Set(P_TESTPLAYER,PROTECTED,F_MODE_AS);
4110 return res;
4111}
4112
4113int zweitiemarkierung(string arg)
4114{
4115 if (!QueryProp(P_SECOND))
4116 return _notify_fail("Aber Du bist doch gar kein Zweiti.\n"),0;
4117 notify_fail("Syntax: zweitiemarkierung [unsichtbar|sichtbar|name]\n");
4118 if (!arg)
4119 return 0;
4120 switch (arg)
4121 {
4122 case "unsichtbar" :
4123 SetProp(P_SECOND_MARK,-1);
4124 write("Jetzt sieht kein Spieler mehr, dass Du ein Zweiti bist.\n");
4125 return 1;
4126 case "sichtbar" :
4127 SetProp(P_SECOND_MARK,0);
4128 write("Jetzt sieht kein Spieler mehr, wessen Zweiti Du bist.\n");
4129 return 1;
4130 case "name" :
4131 SetProp(P_SECOND_MARK,1);
4132 write("Jetzt koennen alle sehen, wessen Zweiti Du bist.\n");
4133 return 1;
4134 }
4135 return 0;
4136}
4137
4138int topliste(string arg)
4139{
4140 if (!arg)
4141 {
4142 printf("Du hast Dich fuer die Topliste %s.\n",
4143 (QueryProp(P_NO_TOPLIST) ? "gesperrt" : "freigegeben"));
4144 return 1;
4145 }
4146 else if (member(({"j","ja","n","nein"}),arg)==-1)
4147 return _notify_fail("Syntax: topliste [ja|nein]\n"),0;
4148 if (arg[0]=='j')
4149 {
4150 SetProp(P_NO_TOPLIST,0);
4151 write("Du kannst jetzt (theoretisch) in der Topliste auftauchen.\n");
4152 }
4153 else
4154 {
4155 SetProp(P_NO_TOPLIST,1);
Zesstradd2d1982017-01-28 14:03:19 +01004156 "/secure/topliste"->DeletePlayer();
4157 write("Du wirst jetzt nicht (mehr) in den Toplisten auftauchen.\n");
MG Mud User88f12472016-06-24 23:31:02 +02004158 }
4159 Set(P_NO_TOPLIST,SAVE|PROTECTED,F_MODE_AS);
4160 return 1;
4161}
4162
4163int show_telnegs(string arg)
4164{
4165 if (!arg)
4166 {
4167 write("Du bekommst Aenderungen Deiner Fenstergroesse "+
4168 (QueryProp(P_TTY_SHOW)?"":"nicht ")+"angezeigt.\n");
4169 return 1;
4170 }
4171 if (member(({"ein","an","aus"}),arg)==-1)
4172 {
4173 write("Syntax: telnegs [ein|aus]\n");
4174 return 1;
4175 }
4176 if (arg=="ein" || arg=="an")
4177 {
4178 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"":"nun ")+
4179 "Aenderungen Deiner Fenstergroesse angezeigt.\n");
4180 Set(P_TTY_SHOW,1);
4181 return 1;
4182 }
4183 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"nun ":"")+
4184 "Aenderungen Deiner Fenstergroesse nicht "+
4185 (QueryProp(P_TTY_SHOW)?"mehr ":"")+"angezeigt.\n");
4186 Set(P_TTY_SHOW,0);
4187 return 1;
4188}
4189
4190private int set_keep_alive(string str) {
4191 if (str == "ein") {
4192 telnet_tm_counter = 240 / __HEART_BEAT_INTERVAL__;
4193 tell_object(this_object(), break_string(
4194 "An Deinen Client werden jetzt alle 4 Minuten unsichtbare Daten "
4195 "geschickt, um zu verhindern, dass Deine Verbindung zum "MUDNAME
4196 " beendet wird.", 78));
4197 }
4198 else if (str == "aus") {
4199 telnet_tm_counter = 0;
4200 tell_object(this_object(),break_string(
4201 "Du hast das Senden von unsichtbaren Daten (Keep-Alive-Pakete) an "
4202 "Deinen Client ausgeschaltet.",78));
4203 }
4204 else {
4205 if (!telnet_tm_counter)
4206 tell_object(this_object(), break_string(
4207 "An Deinen Client werden keine Keep-Alive-Pakete geschickt.",78));
4208 else
4209 tell_object(this_object(), break_string(
4210 "An Deinen Client werden alle 4 Minuten "
4211 "unsichtbare Daten geschickt, damit Deine Verbindung "
4212 "zum "MUDNAME" nicht beendet wird.",78));
4213 }
4214 return 1;
4215}
4216
4217private int print_telnet_rttime() {
4218 int rtt = QueryProp(P_TELNET_RTTIME);
4219 if (rtt>0)
4220 tell_object(ME, break_string(
4221 "Die letzte gemessene 'round-trip' Zeit vom MG zu Deinem Client "
4222 "und zurueck betrug " + rtt + " us.",78));
4223 else
4224 tell_object(ME, break_string(
4225 "Bislang wurde die 'round-trip' Zeit vom MG zu Deinem Client "
4226 "noch nicht gemessen oder Dein Client unterstuetzt dieses "
4227 "nicht.",78));
4228 return 1;
4229}
4230
4231int telnet_cmd(string str) {
4232 if (!str) return 0;
4233 string *args = explode(str, " ");
4234 string newargs;
4235 if (sizeof(args) > 1)
4236 newargs = implode(args[1..], " ");
4237 else
4238 newargs = "";
4239
4240 switch(args[0])
4241 {
4242 case "keepalive":
4243 return set_keep_alive(newargs);
4244 case "rttime":
4245 return print_telnet_rttime();
4246 }
4247 return 0;
4248}
4249
4250int spotte( string str )
4251{
4252 _notify_fail( "Hier ist nichts, was Du verspotten koenntest!\n" );
4253 return 0;
4254}
4255
4256int behalte(string str)
4257{
4258 object ob,*obs;
4259 string s;
4260
4261 if (str)
4262 {
4263 if (str=="alles") {
4264 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, getuid());
4265 write("Ok!\n");
4266 return 1;
4267 }
4268 if (str=="nichts") {
4269 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, 0);
4270 write("Ok!\n");
4271 return 1;
4272 }
4273 if (!sizeof(obs=find_obs(str,PUT_GET_NONE)))
4274 {
4275 _notify_fail("Aber sowas hast Du nicht dabei!\n");
4276 return 0;
4277 }
4278 else ob=obs[0];
4279
4280 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4281 ob->SetProp(P_KEEP_ON_SELL,0);
4282 else
4283 ob->SetProp(P_KEEP_ON_SELL,geteuid(ME));
4284
4285 // erneut abfragen, da sich der Wert nicht geaendert haben muss
4286 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4287 write(break_string(sprintf("Ok, Du wirst %s jetzt bei 'verkaufe alles' "
4288 "behalten.\n",ob->name(WEN)),78));
4289 else
4290 write(break_string(sprintf("Ok, Du wirst %s beim naechsten 'verkaufe "
4291 "alles' mitverkaufen!\n",ob->name(WEN)),78));
4292
4293 return 1;
4294 }
4295 s=make_invlist(ME,filter(all_inventory(ME),#'_check_keep)); //'));
4296 More(s);
4297 return 1;
4298}
4299
4300static int _query_lep()
4301{
4302 int val;
4303 val = LEPMASTER->QueryLEP();
4304 Set( P_LEP, val );
4305 return val;
4306}
4307
4308static mixed _set_fraternitasdonoarchmagorum(mixed arg)
4309{
4310 if (!intp(arg)) return -1;
4311
4312 if ((!previous_object(1)||object_name(previous_object(1))!=FAO_MASTER) &&
4313 (!this_interactive() || !IS_ARCH(this_interactive())))
4314 return -1;
4315
4316 if (!intp(arg)) return -1;
4317
4318 log_file("fao/P_FAO",sprintf("%s - %s P_FAO gesetzt auf %O\n",
4319 dtime(time()),query_real_name(),arg) );
4320 return Set(P_FAO,arg);
4321}
4322
4323nomask void set_realip(string str)
4324{
4325 if(previous_object() && strstr(object_name(previous_object()),"/secure")==0)
4326 {
4327 realip=str;
4328 }
4329}
4330
4331nomask string query_realip()
4332{
4333 return realip ? realip : "";
4334}
4335
4336mixed _query_netdead_env() {
4337 return ndead_location || ndead_l_filename;
4338}