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