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