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