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