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