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