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