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