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