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