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