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