blob: 6d7af1973a50ae4dd4e798eb7b76a998e308a320 [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 */
2214 newflag = !restore_object( "/" + SAVEPATH + lower_case(str)[0..0] + "/"
2215 +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
2280 if ( !IS_LEARNER(ME) && second = QueryProp(P_SECOND) ) {
2281 if ( stringp(second) && lower_case(second)[0..3] == "von " ) {
2282 second = lower_case(second[4..]);
2283 SetProp( P_SECOND, second );
2284 }
2285
2286 if ( !stringp(second ) ||
2287 file_size( "/save/" + second[0..0] + "/" + second + ".o" ) <= 0 ){
2288 if ( stringp(second) &&
2289 file_size( "/save/" + lower_case(second[0..0]) + "/" +
2290 lower_case(second) + ".o" ) >0 ){
2291 SetProp( P_SECOND, lower_case(second) );
2292 log_file( "WRONG_SECOND",
2293 sprintf( "%s: %s: P_SECOND = %O -> Automatisch "
2294 "korrigiert,\n",
2295 dtime(time()), object_name(), second ) );
2296 }
2297 else {
2298 tell_object( ME,
2299 "*\n*\n* Deine Zweitiemarkierung ist ungueltig, "
2300 "bitte aendere diese und sprich im\n* Zweifel "
2301 "bitte einen Erzmagier an.\n*\n*\n" );
2302
2303 log_file( "WRONG_SECOND",
2304 sprintf( "%s: %s: P_SECOND = %O\n",
2305 dtime(time()), object_name(), second ) );
2306 // ein bisschen deutlicher auffordern.. Padreic 08.04.1999
2307 move( "/d/gebirge/room/zwafflad", M_GO );
2308 }
2309 }
2310 }
2311 return(0);
2312}
2313
2314/** Letzte Phase der Spielerinitialisierung beim Laden des Charakters.
2315 * Ruft enable_commands(), aktiviert den Heartbeat und aktiviert die
2316 * Kommandos aus den geerbten command.c, put_and_get.c, team.c, soul.c,
2317 * guide.c, setzt den Living Name.
2318 * Registriert UseSpell() als Catchall-Kommando.
2319 * Laesst den Spieler ggf. einen Level per /std/gilde aufsteigen, ruft
2320 * call_notify_player_change(), loest Login-Event aus.
2321 * Gibt Willkommenstexte, News und neue Mails aus.
2322 * Findet den Startraum und bewegt den Spieler dorthin.
2323 * Ruft FinalSetup() (aus den Rassenshells).
2324 * Begrenzt Geldmenge im Spieler (wegen Bankzweities) nach Tragkraft und
2325 * erstattet Geld bei Reboot.
2326 * Ruft set_is_wizard().
2327 * Startet Clonen der Autoloader.
2328 * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
2329 */
2330private void InitPlayer4()
2331{
2332 int num, str, ski;
2333 string err, called_from_ip;
2334 mixed start_place;
2335 object mon;
2336
2337 enable_commands();
2338 set_heart_beat(1);
2339 command::initialize();
2340 add_put_and_get_commands();
2341 add_team_commands();
2342 add_soul_commands();
2343 add_guide_commands();
2344 add_action( "UseSpell", "", 1 );
2345 set_living_name( getuid() );
2346 while ( remove_call_out("disconnect") != -1 )
2347 ;
2348
2349 if ( QueryProp(P_LEVEL) == -1 )
2350 {
2351 catch( "/std/gilde"->try_player_advance(this_object()) ;publish );
2352 }
2353
2354 if ( interactive(ME) )
2355 call_notify_player_change(1);
2356
2357 if ( interactive(this_object()) ) {
2358 cat( "/etc/NEWS" );
2359
2360 NewbieIntroMsg();
2361
2362 if ( QueryProp(P_INVIS) && !IS_WIZARD(ME) )
2363 SetProp( P_INVIS, 0 );
2364
2365 catch( num = "secure/mailer"->FingerMail(getuid());publish );
2366
2367 if ( num )
2368 write( "Du hast " + num + " neue" +
2369 (num == 1 ? "n Brief" : " Briefe")+" im Postamt liegen.\n" );
2370
2371 catch( RegisterChannels();publish );
2372
2373 if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
2374 query_ip_number(ME) != called_from_ip ){
2375 string tmp;
2376
2377 if ( stringp(tmp = query_ip_name(called_from_ip)) &&
2378 tmp != called_from_ip )
2379 tmp = " [" + tmp + "]";
2380 else
2381 tmp = "";
2382
2383 write( "Das letzte Mal kamst Du von " + called_from_ip
2384 + tmp + ".\n" );
2385 }
2386
2387 Set( P_CALLED_FROM_IP, query_ip_number(ME) );
2388 }
2389
2390 if ( !stringp(default_home) || default_home == "" )
2391 default_home = "/gilden/abenteurer";
2392
2393 if ( IS_SEER(ME) && !IS_LEARNER(ME) )
2394 catch( start_place = HAUSVERWALTER->FindeHaus(getuid(ME));publish );
2395 // wenn der Spieler noch ganz frisch ist und noch wenig Stufenpunkte
2396 // gekriegt hat und das Tutorial noch nicht gemacht hat, startet er im
2397 // Tutorial.
2398 else if (QueryProp(P_LEP) <= 120
2399 && QM->HasMiniQuest(this_object(),
2400 "/d/anfaenger/ennox/tutorial/npcs/verkaeufer") != 1)
2401 start_place = "/room/welcome/"+ getuid(this_object());
2402 else
2403 start_place = 0;
2404
2405 if ( !start_place )
2406 start_place = QueryProp(P_START_HOME);
2407
2408 if( objectp(start_place) || (stringp(start_place) && start_place != "" ) ){
2409 if ((err = catch(move( start_place, M_GO|M_SILENT|M_NO_SHOW );publish))
2410 || !environment() )
2411 err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
2412 }
2413 else
2414 err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
2415
2416 if ( err )
2417 catch(move( "/gilden/abenteurer", M_GO|M_SILENT|M_NO_SHOW );publish);
2418
2419 // Die Shell muss FinalSetup() nicht implementieren. Daher Callother
2420 catch( ME->FinalSetup();publish );
2421
2422 // Login-event ausloesen
2423 EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
2424 E_OBJECT: ME,
2425 E_PLNAME: getuid(ME),
2426 E_ENVIRONMENT: environment() ]) );
2427
2428 // erst jetzt GMCP freigeben und zu verhandeln.
2429 gmcp::startup_telnet_negs();
2430
2431 // Schonmal sichern, falls ein Bug (Evalcost...) dazwischen kommen sollte.
2432 autoload_rest = autoload;
2433
2434 // Um Geld-Xties mit illegal viel Geld zu vermeiden, kommt ein Check:
2435 if ( !IS_LEARNER(ME) && !Query(P_TESTPLAYER) &&
2436 mon = present( "\ngeld", ME ) ){
2437 // maximale Kraft, die der Spieler haette haben koennen
2438 str = QueryAttribute(A_STR) + 4;
2439 if ( Query(P_FROG) )
2440 str += 30;
2441 if ( str < 1 )
2442 str = QueryRealAttribute(A_STR) + 4;
2443 if ( str > 30 )
2444 str = 30;
2445
2446 // Trageskill beachten
2447 ski = to_int(UseSkill( SK_CARRY, ([SI_SKILLARG : str ]) ));
2448 if ( !intp(ski) )
2449 ski = 0;
2450
2451 // Wieviel konnte der Spieler insgesamt maximal tragen?
2452 num = 9200 + str * 800 + ski;
2453 if ( num < 3000 )
2454 num = 3000;
2455
2456 // Verdoppeln fuer einen guten Container und nochmal 20% draufschlagen
2457 // zur Sicherheit. Das Ganze danach *4, um die maximale Anzahl Muenzen
2458 // zu erhalten.
2459 num = (int) (num * 8.8);
2460
2461 // Geld, das zuviel ist, auf den Maximalwert kuerzen.
2462 // Zur Sicherheit wird mitgeloggt. Falls ein Spieler sich (zu recht)
2463 // beschwert, kann man immer noch wieder die Summe korrigieren ;-)
2464 if ( (str = mon->QueryProp(P_AMOUNT)) > num ){
2465 mon->SetProp( P_AMOUNT, num );
2466 log_file( "ZUVIEL_GELD", sprintf( "%s: %s hatte %d Muenzen bei "
2467 "sich. Korrigiert auf %d.\n ",
2468 ctime(time())[4..],
2469 capitalize(getuid(ME)),
2470 str, num ) );
2471 }
2472 }
2473 int entschaedigung = QueryProp(P_CARRIED_VALUE);
2474 if ( entschaedigung > 0 )
2475 {
2476 write( "Du findest " + entschaedigung +
2477 " Muenzen, die Du beim letzten Mal verloren hast.\n" );
2478
2479 if ( MayAddWeight( entschaedigung / 4 ) ){
2480 write( "Weil Du nicht mehr soviel tragen kannst, spendest Du den "+
2481 "Rest der Zentralbank.\n" );
2482
2483 num = (QueryProp(P_MAX_WEIGHT) - query_weight_contents()) * 4;
2484 entschaedigung = (num < 0) ? 0 : num;
2485 }
2486
2487 AddMoney( entschaedigung );
2488 SetProp(P_CARRIED_VALUE,0);
2489 }
2490
2491 if ( !QueryProp(P_INVIS) )
2492 say( capitalize(name(WER)) + " betritt diese Welt.\n" );
2493 else
2494 write( "DU BIST UNSICHTBAR!\n\n" );
2495#if __EFUN_DEFINED__(set_is_wizard)
2496 if ( IS_WIZARD(getuid(ME)) )
2497 set_is_wizard( ME, 1 );
2498 else
2499 set_is_wizard( ME, 0 );
2500#endif
2501 if ( query_once_interactive(ME) )
2502 ListAwaited();
2503
2504 // Autoloader werden ganz zum Schluss geclont, da das bis zum bitteren
2505 // (Evalcost-)Ende geschieht und danach u.U. keine Rechenzeit fuer
2506 // andere Aktionen mehr ueber ist
2507 load_auto_objects( autoload );
2508}
2509
2510/** Setzt die Spielerinitialisierung nach start_player() fort.
2511 * Prueft den Wert der vom Spieler getragenen Sachen (fuer Entschaedigung
2512 * nach Reboot).
2513 * Setzt div. Properties des "Spielerkoerpers", die eigentlich gespeichert
2514 * werden, nach einem Reboot zurueck.
2515 * Fragt ggf. nach eMail-Adresse und uebergibt per input_to an
2516 * InitPlayer2().
2517 * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
2518*/
2519private void InitPlayer()
2520{
2521 string mailaddr;
2522
2523 // wenn es einen Crash gab, sollen Spieler nicht noch extra bestraft werden
2524 if ( file_time( "/save/" + getuid()[0..0] + "/" + getuid() + ".o" )
2525 < last_reboot_time() ){
2526 SetProp( P_FOOD, 0 );
2527 SetProp( P_DRINK, 0 );
2528 SetProp( P_ALCOHOL, 0 );
2529 SetProp( P_BLIND, 0 );
2530 SetProp( P_DEAF, 0 );
2531 SetProp( P_POISON, 0 );
2532 SetProp( P_GHOST, 0 );
2533 SetProp( P_FROG, 0 );
2534 SetProp( P_HP, QueryProp(P_MAX_HP) );
2535 SetProp( P_SP, QueryProp(P_MAX_SP) );
2536 }
2537
2538 if ( QueryGuest() )
2539 Set( P_MAILADDR, "none" );
2540 else if ( !(mailaddr = Query(P_MAILADDR)) || mailaddr == "" ) {
2541 write(break_string(
2542 "Eine gueltige EMail-Adresse erleichtert es erheblich, Dir "
2543 "ein neues Passwort setzen zu lassen, falls Du einmal Dein "
2544 "Passwort vergisst.",78));
2545 input_to( "getmailaddr",INPUT_PROMPT,
2546 "Gib bitte Deine EMail-Adresse an: " );
2547 return;
2548 }
2549 InitPlayer2();
2550}
2551
2552/** liest eMail-Adresse vom Spieler ein und speichert sie.
2553 * Uebergibt anschliessend an InitPlayer2()
2554 * @param[in] maddr Spielereingabe der Emailadresse.
2555 * @sa InitPlayer2().
2556 */
2557static void getmailaddr( string maddr )
2558{
2559 maddr = check_email(maddr);
2560
2561 if ( !stringp(maddr)) {
2562 write("Deine Eingabe scheint keine gueltige EMail-Adresse gewesen "
2563 "zu sein.\n");
2564 input_to( "getmailaddr", INPUT_PROMPT,
2565 "Gib bitte Deine EMail-Adresse an: " );
2566 return;
2567 }
2568 Set( P_MAILADDR, maddr );
2569 InitPlayer2();
2570}
2571
2572
2573/** Prueft Geschlecht des Spielers und fragt ggf. beim Spieler nach.
2574 * Uebergibt an InitPlayer3() oder get_gender().
2575 * @sa InitPlayer3(), get_gender().
2576 */
2577private void InitPlayer2()
2578{
2579 if( member(({ MALE, FEMALE }), QueryProp(P_GENDER) ) == -1 ) {
2580 input_to( "getgender", INPUT_PROMPT,
2581 "Bist Du maennlich oder weiblich: ");
2582 return;
2583 }
2584
2585 InitPlayer3();
2586}
2587
2588/** Liest Spielerantwort auf die Frage nach dem Geschlecht des Chars ein.
2589 * Wird gerufen von input_to().
2590 * Uebergibt an InitPlayer3().
2591 * @sa InitPlayer3().
2592 */
2593static void getgender( string gender_string )
2594{
2595 gender_string = lower_case( gender_string );
2596
2597 if ( sizeof(gender_string)==1 && gender_string[0] == 'm' ){
2598 write( "Willkommen, mein Herr!\n" );
2599 SetProp( P_GENDER, MALE );
2600 }
2601 else if ( sizeof(gender_string)==1 && gender_string[0] == 'w' ){
2602 write( "Willkommen, gnae' Frau!\n" );
2603 SetProp( P_GENDER, FEMALE );
2604 }
2605 else {
2606 write( "Wie? Was? Verstehe ich nicht!\n" );
2607 input_to( "getgender", INPUT_PROMPT,
2608 "Bist Du maennlich oder weiblich? (tippe m oder w): ");
2609 return;
2610 }
2611
2612 InitPlayer3();
2613}
2614
2615
2616/** Prueft Terminaltyp des Spielers und fragt ggf. beim Spieler nach.
2617 * Uebergibt an InitPlayer4() oder gettty().
2618 * @sa InitPlayer4(), gettty().
2619 */
2620private void InitPlayer3()
2621{
2622 if ( !QueryProp(P_TTY) || QueryProp(P_TTY) == "none" )
2623 {
2624 write( "Waehle einen Terminaltyp (kann spaeter mit <stty> geaendert "
2625 "werden)\n");
2626 input_to( "gettty", INPUT_PROMPT, "vt100, ansi, dumb (Standard: dumb): " );
2627 return;
2628 }
2629 InitPlayer4();
2630}
2631
2632/** Liest Spielerantwort auf die Frage nach dem Terminaltyp ein.
2633 * Wird gerufen von input_to().
2634 * Uebergibt an InitPlayer4().
2635 * @sa InitPlayer4().
2636 */
2637static void gettty( string ttystr )
2638{
2639 if ( !ttystr || ttystr == "" )
2640 ttystr = "dumb";
2641
2642 ttystr = lower_case(ttystr);
2643
2644 if ( ttystr == "vt100" ){
2645 write( "Dies sollte " + ANSI_BOLD + "fett" + ANSI_NORMAL + " sein.\n" );
2646 SetProp( P_TTY, ttystr );
2647 }
2648 else
2649 if ( ttystr == "ansi" ){
2650 write( "Dies sollte " + ANSI_RED + "rot" + ANSI_NORMAL +
2651 " sein.\n" );
2652 SetProp( P_TTY, "ansi" );
2653 }
2654 else if ( ttystr == "dumb" ){
2655 write( "Ohje, oede! Besorg Dir ein besseres Terminal!\n" );
2656 SetProp( P_TTY, "dumb" );
2657 }
2658 else {
2659 write( "Dieser Terminaltyp wird nicht unterstuetzt. Nimm bitte "
2660 "einen aus:\nvt100, ansi or dumb (Standard ist dumb).\n" );
2661 input_to( "gettty", INPUT_PROMPT,
2662 "vt100, ansi or dumb (Standard ist dumb): ");
2663 return;
2664 }
2665
2666 InitPlayer4();
2667}
2668
2669
2670/** Liefert die UID des Charakters zurueck, also den Charakternamen.
2671 * @return UID des Objekts (Charaktername).
2672 */
2673nomask string query_real_name() {
2674 /* ACHTUNG !! DIES LFUN DARF NICHT ENTFERNT WERDEN !!! */
2675 /* Sie wird vom Gamedriver (zB bei F_ED) aufgerufen !! */
2676 /* Ich bin da zwar nicht so ueberzeugt von, dass der Driver die heutzutage
2677 * noch ruft, aber die halbe Mudlib ruft sie. ;-) (Zesstra, 27.4.08)
2678 */
2679 return getuid();
2680}
2681
2682/*
2683 * the wizard command review: show player moving messages
2684 */
2685int review() {
2686 string *msg;
2687 write(short());
2688 write("Deine Bewegungen werden wie folgt gemeldet:\n"+
2689 "mout: "+name(WER)+" "+(msg=explode(QueryProp(P_MSGOUT),"#"))[0]
2690 +" <Richtung>"+(sizeof(msg)>1 ? msg[1] : "")+".\n"+
2691 "min: "+name(WER)+" "+QueryProp(P_MSGIN)+".\n"+
2692 "mmout: "+name(WER)+" "+QueryProp(P_MMSGOUT)+".\n"+
2693 "mmin: "+name(WER)+" "+QueryProp(P_MMSGIN)+".\n"+
2694 (IS_LEARNER(ME) ?
2695 "cmsg: "+name(WER)+" "+QueryProp(P_CLONE_MSG)+".\n"+
2696 "dmsg: <Irgendetwas> "+QueryProp(P_DESTRUCT_MSG)+".\n"
2697 : "")+
2698 "Wenn Du jemanden angreifst, sieht das so aus:\n"+
2699 name(WER)+" greift Dich"+QueryProp(P_HANDS)[0]+" an.\n");
2700 return 1;
2701}
2702
2703/*
2704 * set player moving messages
2705 */
2706
2707static int setmin(string str)
2708{
2709 SetProp(P_MSGIN, _unparsed_args()||"kommt an");
2710 write("Ok.\n");
2711 return 1;
2712}
2713
2714static int setmout(string str)
2715{
2716 string *msg;
2717
2718 if(sizeof(msg=explode((_unparsed_args()||"geht"),"#"))>2)
2719 {
2720 write("Du darfst nur einmal '#' fuer die Richtung angeben.\n");
2721 return 1;
2722 }
2723 if(sizeof(msg)>1)
2724 {
2725 if (msg[0]!="" && msg[0][<1]==' ') msg[0]=msg[0][0..<2];
2726 SetProp(P_MSGOUT, msg[0]+"#"+msg[1]);
2727 }
2728 else
2729 SetProp(P_MSGOUT, _unparsed_args()||"geht");
2730 write("Ok.\n");
2731 return 1;
2732}
2733
2734static int setmmin(string str)
2735{
2736 SetProp(P_MMSGIN, _unparsed_args()||"erscheint");
2737 write("Ok.\n");
2738 return 1;
2739}
2740
2741static int setmmout(string str)
2742{
2743 SetProp(P_MMSGOUT, _unparsed_args()||"verschwindet");
2744 write("Ok.\n");
2745 return 1;
2746}
2747
2748static int setcmsg(string str)
2749{
2750 SetProp(P_CLONE_MSG, _unparsed_args()||"zaubert etwas aus "
2751 + QueryPossPronoun(MALE,WEM) + " Aermel hervor");
2752 write("Ok.\n");
2753 return 1;
2754}
2755
2756static int setdmsg(string str)
2757{
2758 SetProp(P_DESTRUCT_MSG, _unparsed_args()||"wird von " + name(WER,1)
2759 + " zerstaeubt");
2760 write("Ok.\n");
2761 return 1;
2762}
2763
2764static int set_title(string str)
2765{
2766 string bonus;
2767
2768 if(!IS_SEER(this_object()) && !FAO_HAS_TITLE_GIFT(this_object()) )
2769 {
2770 return 0;
2771 }
2772
2773 bonus = "";
2774 if (!(str=_unparsed_args()))
2775 str = "";
2776 else if( str[0..2]=="\\b," || str[0..2]=="\\b'" )
2777 {
2778 bonus = "\b"; // ein backspace fuer ein (hoch)komma ist ok! :-)
2779 str = str[2..];
2780 }
2781 if(str=="0") // damit der Gildentitel zum Vorschein kommen kann
2782 SetProp(P_TITLE, 0);
2783 else
2784 SetProp(P_TITLE, bonus+str);
2785 write("Ok.\n");
2786 return 1;
2787}
2788
2789static int extra_input(string str, string look)
2790{
2791 if (str=="**")
2792 {
2793 if (look=="")
2794 SetProp(P_EXTRA_LOOK,0);
2795 else
2796 SetProp(P_EXTRA_LOOK,look);
2797 write("Ok.\n");
2798 return 1;
2799 }
2800 input_to("extra_input",INPUT_PROMPT, "]" ,look+str+"\n");
2801 return 1;
2802}
2803
2804static int extralook(mixed str)
2805{
2806 if( str=="?" )
2807 {
2808 write( "Dein Extralook ist : "+QueryProp(P_EXTRA_LOOK) + "\n");
2809 return 1;
2810 }
2811 write("Bitte gib Deinen Extra-Look ein. Beenden mit **:\n");
2812 input_to("extra_input",INPUT_PROMPT,"]","");
2813 return 1;
2814}
2815
2816static void calculate_value()
2817{
2818 int i, carried_value, value;
2819
2820 carried_value=0;
2821 foreach(object ob: deep_inventory(ME)) {
2822 if (!ob->QueryProp(P_AUTOLOADOBJ))
2823 carried_value+=((value=(int)ob->QueryProp(P_VALUE)) > 1000 ? 1000 : value);
2824 }
2825 SetProp(P_CARRIED_VALUE, carried_value);
2826}
2827
2828/* Called by command 'save' */
2829int save_character() {
2830 save_me(1);
2831 write("Ok.\n");
2832 return 1;
2833}
2834
2835void save_me(mixed value_items)
2836{
2837 // Gaeste werden nicht gespeichert
2838 if( getuid()[0..3]=="gast" )
2839 return;
2840
2841 // Autoloader identifizieren und speichern
2842 autoload=([]);
2843 foreach(object ob: deep_inventory(ME)) {
2844 int val = ob->QueryProp( P_AUTOLOADOBJ );
2845 if (val && clonep(ob))
2846 {
2847 string obname=load_name(ob);
2848 if (obname == GELD)
2849 autoload[obname] += val;
2850 else
2851 autoload += ([obname:val]);
2852 }
2853 }
2854 // An noch nicht geclonte Autoloader denken!
2855 autoload += autoload_rest;
2856
2857 // Im Bedarfsfall Wert des Inventory bestimmen
2858 if (value_items)
2859 calculate_value();
2860 else
2861 SetProp(P_CARRIED_VALUE, 0);
2862
2863 // Logout-Zeit speichern
2864 if(query_once_interactive(ME) && !QueryProp(P_INVIS))
2865 Set(P_LAST_LOGOUT,time());
2866
2867 // Funktion zur Bestimmung des Gildenrating aufrufen
2868 string gilde=GUILD_DIR+QueryProp(P_GUILD);
2869 if (find_object(gilde) || file_size(gilde+".c")>-1)
2870 catch(call_other(gilde,"GuildRating",this_object());publish);
2871
2872 // Speichern des Spielers
2873 save_object("/"+SAVEPATH+getuid()[0..0]+"/" + getuid());
2874}
2875
2876static varargs void log_autoload( string file, string reason, mixed data, string error )
2877{
2878 if (member(autoload_error,file)!=-1) return;
2879 log_file(SHELLLOG("NO_AUTO_FILE"),sprintf("%s: %s: %s\nreason: cannot %s file\ndata: %O\n%s\n",
2880 ctime()[4..15],capitalize(getuid()),file,reason,data,
2881 (error?"Fehlermeldung: "+error+"\n":"")));
2882 autoload_error+=({file});
2883}
2884
2885// tics, die fuer autoloader reichen sollten:
2886#define SAFE_FOR_AUTOLOADER __MAX_EVAL_COST__/4
2887
2888private void load_auto_object( string file, mixed data )
2889{
2890 object ob;
2891 string error;
2892
2893 if( get_eval_cost() < SAFE_FOR_AUTOLOADER ) return;
2894 m_delete( autoload_rest, file );
2895 autoload_error-=({file});
2896
2897 if ( file == "/obj/money" )
2898 file = "/items/money";
2899 if ( file == "/obj/seercard" )
2900 file = "/items/seercard";
2901
2902 ob = find_object(file);
2903
2904 if (!ob)
2905 {
2906 if (file_size(file+".c")<0&&
2907 file_size(implode(explode(file,"/")[0..<2],"/")+
2908 "/virtual_compiler.c")<0)
2909 {
2910 log_autoload(file,"find",data,0);
2911 return;
2912 }
2913 if (error = catch(load_object(file); publish))
2914 {
2915 log_autoload(file,"load",data,error);
2916 return;
2917 }
2918 }
2919 if ( error = catch(ob = clone_object(file); publish) )
2920 {
2921 log_autoload(file,"clone",data, error);
2922 return;
2923 }
2924
2925 if ( error = catch(ob->SetProp( P_AUTOLOADOBJ, data ); publish) )
2926 {
2927 log_autoload(file,"SetProp",data, error);
2928 ob->remove(1);
2929 if (ob) destruct(ob);
2930 return;
2931 }
2932
2933 if ( error = catch(ob->move( ME, M_NOCHECK );publish) ) {
2934 log_autoload(file,"move",data, error);
2935 ob->remove(1);
2936 if(ob) destruct(ob);
2937 return;
2938 }
2939}
2940
2941static void load_auto_objects( mapping map_ldfied )
2942{
2943 if ( (!mappingp(map_ldfied) || !sizeof(map_ldfied)) && !sizeof(autoload_rest) )
2944 return;
2945
2946 // Mit Netztoten Spielern rechnen manche Autoloader nicht. Also
2947 // das Clonen unterbrechen und in Reconnect() wieder anwerfen.
2948 if ( !interactive() )
2949 return;
2950
2951 // Kleiner Hack: autoload_rest ist eine globale Variable, die beim
2952 // Clonen der einzelnen Autoloader direkt veraendert wird.
2953 // So lange das Mapping noch Eintraege hat, muessen wir noch fehlende
2954 // Autoloader clonen.
2955 if ( !sizeof(autoload_rest) )
2956 autoload_rest = map_ldfied;
2957
2958 // Schon hier einen call_out() zum "Nach"clonen von noch nicht geclonten
2959 // Autoloadern starten, da spaeter u.U. keine Rechenzeit mehr dafuer da ist.
2960 while ( remove_call_out("load_auto_objects") != -1 )
2961 /* do nothing */;
2962
2963 // Mit Parameter '0' aufrufen, da das globale Mapping benutzt wird.
2964 // Verzoegerung 0 in rekursiven Callouts ist bloed, also 1s Delay
2965 call_out( "load_auto_objects", 2, 0 );
2966
2967 // Mit catch() gegen die Evalcost-Falle!
2968 // Mit Absicht das walk_mapping() aus der "alten" Version erhalten und
2969 // nicht durch eine (einfachere) Schleife inkl. get_eval_cost() ersetzt,
2970 // da eine Schleife gegenueber der walk_mapping()-Loesung den Aufbau
2971 // der previous_object()-Kette veraendern wuerde; darauf testen aber
2972 // manche Objekte.
2973 catch( walk_mapping( autoload_rest, #'load_auto_object/*'*/ ) );
2974}
2975
2976/*
2977 * al_to_title: Make the numeric alignment value into a string
2978 */
2979static string al_to_title(int a)
2980{
2981 if (a >= KILL_NEUTRAL_ALIGNMENT * 100)
2982 return "heilig";
2983 if (a > KILL_NEUTRAL_ALIGNMENT * 20)
2984 return "gut";
2985 if (a > KILL_NEUTRAL_ALIGNMENT * 4)
2986 return "nett";
2987 if (a > - KILL_NEUTRAL_ALIGNMENT * 4)
2988 return "neutral";
2989 if (a > - KILL_NEUTRAL_ALIGNMENT * 20)
2990 return "frech";
2991 if (a > - KILL_NEUTRAL_ALIGNMENT * 100)
2992 return "boese";
2993 return "satanisch";
2994}
2995
2996static int toggle_whimpy_dir(string str) {
2997 SetProp(P_WIMPY_DIRECTION,str=_unparsed_args()||str);
2998 if (str)
2999 printf("Ok, Fluchtrichtung %O.\n",str);
3000 else
3001 printf("Ok, bevorzugte Fluchtrichtung deaktiviert.\n");
3002 return 1;
3003}
3004
3005static int toggle_whimpy(string str)
3006{
3007 int i;
3008
3009 if(!str || str=="" || (sscanf(str,"%d",i)<0))
3010 {
3011 write("vorsicht <hp>, 0<=hp<"+QueryProp(P_MAX_HP)+"\n");
3012 return 1;
3013 }
3014 if(i>=QueryProp(P_MAX_HP) || i<0)
3015 {
3016 write("Der Wert ist nicht erlaubt.\n");
3017 return 1;
3018 }
3019 if(!i) write("Prinz Eisenherz-Modus.\n");
3020 else write("Vorsicht-Modus ("+i+")\n");
3021 SetProp(P_WIMPY,i);
3022 return 1;
3023}
3024
3025/** Bestimmt, ob das Spielerobjekt beschattet werden darf.
3026 * Beschatten ist nur fuer Objekte erlaubt, die in /std/player/shadows
3027 * abgelegt sind.
3028 * Ausnahme: Testspieler mit gesetztem P_ALLOWED_SHADOW
3029 * @param[in] obj Objekt, was beschatten moechte.
3030 * @return 0, wenn Beschatten erlaubt, 1 sonst.
3031 */
3032varargs nomask int query_prevent_shadow(object obj)
3033{
3034 string what, allowed_shadow;
3035 int dummy;
3036
3037// if ( Query(P_TESTPLAYER) )
3038// return 0;
3039
3040 if (obj){
3041 what=object_name(obj);
3042 if (what && what != "" &&
3043 sscanf(what,"/std/player/shadows/%s#%d",what,dummy)==2)
3044 return 0;
3045
3046 // Einem Testspieler kann man P_ALLOWED_SHADOW auf einen zu testenden
3047 // Shadow setzen.
3048 if ( Query(P_TESTPLAYER) &&
3049 stringp(what) &&
3050 stringp(allowed_shadow=Query(P_ALLOWED_SHADOW)) &&
3051 strstr(what, allowed_shadow)==0)
3052 return 0;
3053 }
3054 return 1;
3055}
3056
3057static int uhrzeit()
3058{
3059 write(dtime(time()+QueryProp(P_TIMEZONE)*3600)+".\n");
3060 return 1;
3061}
3062
3063protected string SetDefaultHome(string str)
3064{
3065 return default_home=str;
3066}
3067
3068string QueryDefaultHome()
3069{
3070 return default_home;
3071}
3072
3073protected string SetDefaultPrayRoom(string str)
3074{
3075 if(hc_play>1)
3076 {
3077 default_pray_room="/room/nirvana";
3078 }
3079 else
3080 default_pray_room=str;
3081
3082 return default_pray_room;
3083}
3084
3085string QueryPrayRoom()
3086{
3087 if(hc_play>1)
3088 {
3089 return "/room/nirvana";
3090 }
3091 string room = QueryProp(P_PRAY_ROOM);
3092 if (stringp(room))
3093 return room;
3094 else if (default_pray_room)
3095 return default_pray_room;
3096 // hoffentlich ist das wenigstens gesetzt.
3097 return default_home;
3098}
3099
3100void _restart_beat()
3101{
3102 tell_object(ME,
3103 "Der GameDriver teilt Dir mit: Dein Herzschlag hat wieder eingesetzt.\n");
3104 set_heart_beat(1);
3105}
3106
3107static int weg(string str)
3108{
3109 if (!(str=_unparsed_args()))
3110 {
3111 printf("Du bist nicht%s als abwesend gekennzeichnet.\n",
3112 QueryProp(P_AWAY) ? " mehr" : "");
3113 SetProp(P_AWAY, 0);
3114 return 1;
3115 }
3116 write("Du bist jetzt als abwesend gekennzeichnet.\n");
3117 SetProp(P_AWAY, str);
3118 return 1;
3119}
3120
3121/* Ein Befehl zum anschauen der Wegmeldung anderer Spieler */
3122static int wegmeldung(string player)
3123{
3124
3125 object player_ob;
3126 string weg;
3127
3128 if ( !player || player=="" ||
3129 player==lowerstring(this_player()->QueryProp(P_NAME)))
3130 {
3131 weg=this_player()->QueryProp(P_AWAY);
3132 write ("Du bist "+(weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
3133 if (weg)
3134 write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
3135 return 1;
3136 }
3137
3138 // Welcher Spieler ist gemeint?
3139 player_ob=find_player(player);
3140
3141 // Spieler nicht da oder Invis und Anfrager is kein Magier
3142 if (!player_ob ||
3143 (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
3144 {
3145 write(capitalize(player)+" ist gerade nicht im Spiel.\n");
3146 return 1;
3147 }
3148
3149 weg=player_ob->QueryProp(P_AWAY);
3150
3151 // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
3152 write (player_ob->QueryProp(P_NAME)+" ist "+
3153 (weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
3154
3155 if (weg)
3156 write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
3157
3158 return 1;
3159}
3160
3161static string timediff(int time)
3162{
3163 string ret;
3164
3165 ret="";
3166 if(time>=86400) {
3167 ret+=time/86400+"d ";
3168 time%=86400;
3169 }
3170 if(time<36000) ret+="0";
3171 ret+=time/3600+":";
3172 time%=3600;
3173 if(time<600) ret+="0";
3174 ret+=time/60+":";
3175 time%=60;
3176 if(time<10) ret+="0";
3177 ret+=time+"";
3178 return ret;
3179}
3180
3181
3182/* Ein Befehl zum anschauen der Idlezeit anderer Spieler */
3183static int idlezeit(string player)
3184{
3185
3186 object player_ob;
3187 int idle;
3188
3189 if ( !player || player=="" ||
3190 player==lowerstring(this_player()->QueryProp(P_NAME)))
3191 {
3192 write ("Du bist selber natuerlich gerade nicht idle.\n");
3193 return 1;
3194 }
3195
3196 // Welcher Spieler ist gemeint?
3197 player_ob=find_player(player);
3198
3199 // Spieler nicht da oder Invis und Anfrager is kein Magier
3200 if (!player_ob ||
3201 (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
3202 {
3203 write(capitalize(player)+" ist gerade nicht im Spiel.\n");
3204 return 1;
3205 }
3206
3207 idle=query_idle(player_ob);
3208
3209 // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
3210 write (player_ob->QueryProp(P_NAME)+" ist "+
3211 (idle>=60?timediff(idle):"nicht")+" passiv.\n");
3212
3213 return 1;
3214}
3215
3216
3217/** Belebt einen netztoten Spieler wieder.
3218 * Reaktiviert Heartbeats, bewegt den Spieler zurueck an den Ort, der eim
3219 * Einschlafen zum Aufwachen ermittelt wurde (im einfachsten Fall der Raum,
3220 * wo man eingeschlafen ist).
3221 */
3222static void ndead_revive()
3223{
3224 string fname;
3225 int ret;
3226
3227 set_heart_beat(1);
3228 ndead_next_check = NETDEAD_CHECK_TIME;
3229 ndead_currently = 0;
3230 ndead_lasttime = 0;
3231
3232 if ( !objectp(ndead_location) &&
3233 stringp(ndead_l_filename) && sizeof(ndead_l_filename)) {
3234
3235 if ( member( ndead_l_filename, '#' ) == -1 ){
3236 catch(load_object( ndead_l_filename); publish);
3237 ndead_location = find_object(ndead_l_filename);
3238 }
3239 else {
3240 ndead_location = find_object(ndead_l_filename);
3241 if ( !ndead_location && env_ndead_info ){
3242 fname = explode( ndead_l_filename, "#" )[0];
3243 catch(ndead_location =
3244 (load_object(fname)->SetProp(P_NETDEAD_INFO, env_ndead_info));
3245 publish);
3246 if ( !objectp(ndead_location) ){
3247 catch(load_object( ndead_location);publish);
3248 ndead_location = find_object(ndead_location);
3249 }
3250 }
3251 }
3252 }
3253
3254 if ( !objectp(ndead_location)
3255 || catch(ret = move( ndead_location, M_GO|M_SILENT );publish)
3256 || ret != 1 ) {
3257 move( "gilden/abenteurer", M_GO|M_SILENT );
3258 ndead_location = environment();
3259 }
3260
3261 // ndead_location=0;
3262 ndead_l_filename = 0;
3263 env_ndead_info = 0;
3264}
3265
3266/** Bewegt einen netztoten Spieler in den Netztotenraum
3267 * Gerufen von heartbeat().
3268 * Zerstoert Gaeste, verlaesst ggf. das Team, ermittelt, ob der Spieler beim
3269 * Aufwachen in das alte Environment bewegt werden soll oder in einen anderen
3270 * Raum, hierzu wird im Environment P_NETDEAD_INFO abgefragt.
3271 * Deaktiviert die Kommandos per disable_commands().
3272 */
3273static void ndead_move_me() {
3274 object team;
3275 mixed amem;
3276
3277 set_heart_beat(0);
3278 stop_heart_beats();
3279 if (QueryGuest()) {
3280 quit();
3281 if (ME)
3282 remove();
3283 if (ME)
3284 destruct(ME);
3285 return;
3286 }
3287 ndead_next_check=NETDEAD_CHECK_TIME;
3288 ndead_currently=1;
3289 ndead_lasttime=0;
3290 ndead_location=environment();
3291 if (objectp(ndead_location))
3292 ndead_l_filename=object_name(ndead_location);
3293
3294 if (objectp(environment())
3295 && sizeof(explode(object_name(environment()),"#")) > 1)
3296 env_ndead_info=environment()->QueryProp(P_NETDEAD_INFO);
3297 else
3298 env_ndead_info=0;
3299
3300 if ( objectp(team=Query(P_TEAM)) )
3301 // Der Test auf assoziierte Teammitglieder (== FolgeNPCs)
3302 // verhindert, dass Spieler nach "schlafe ein" aus dem
3303 // Team ausgetragen werden. -- 29.01.2002 Tiamak
3304 // && !objectp(amem=Query(P_TEAM_ASSOC_MEMBERS))
3305 // && !(pointerp(amem) && sizeof(amem)))
3306 team->RemoveMember(ME);
3307
3308 disable_commands();
3309 move(NETDEAD_ROOM,M_GO|M_NO_ATTACK|M_NOCHECK,"ins Reich der Netztoten");
3310}
3311
3312/** Ist dieser Character ein Gast?
3313 * @return 1, wenn Gast, 0 sonst.
3314 */
3315int QueryGuest()
3316{
3317 string dummy;
3318 return sscanf(getuid(),"gast%d",dummy);
3319}
3320
3321/** Spielerkommando 'schlafe ein'.
3322 * Ruft remove_interactive() bei Spielern, bei Magiern wird quit() gerufen,
3323 * um das Magierobjekt zu zerstoeren.
3324 * @sa quit()
3325 */
3326int disconnect(string str)
3327{
3328 string verb;
3329 string desc = break_string(
3330 "\"schlafe ein\" beendet Deine Verbindung mit "MUDNAME". Du behaeltst "
3331 "Deine Sachen.\nFalls "MUDNAME" jedoch abstuerzt oder neu gestartet "
3332 "wird, waehrend Du weg bist, verlierst Du die meisten allerdings "
3333 "(genauso, als wenn Du Deine Verbindung mit \"ende\" beendet haettest). "
3334 "In diesem Fall bekommst Du dann eine kleine Entschaedigung."
3335 ,78,0,BS_LEAVE_MY_LFS);
3336
3337 verb=query_verb();
3338 if (!verb)
3339 verb="AUTOCALL";
3340 if (verb[0..5]=="schlaf" && str!="ein")
3341 {
3342 notify_fail(desc);
3343 return 0;
3344 }
3345 if (IS_LEARNER(this_object()))
3346 return quit();
3347
3348 tell_object(this_object(), desc);
3349
3350 if (clonep(environment()) && !environment()->QueryProp(P_NETDEAD_INFO))
3351 tell_object(this_object(),break_string(
3352 "\nACHTUNG: Wenn Du hier laengere Zeit schlaefst, "
3353 "kommst Du vermutlich nicht an diesen Ort zurueck!",78));
3354
3355 say(capitalize(name(WER))+" hat gerade die Verbindung zu "MUDNAME" gekappt.\n");
3356 remove_interactive(ME);
3357 call_out(#'clear_tell_history,4);
3358 return 1;
3359}
3360
3361static int finger (string str)
3362{
3363 string ret;
3364 mixed xname;
3365
3366 if (!str || str==""
3367 || sizeof(explode(str," ")-({"-n","-p","-s","-v","-a"}))>1)
3368 {
3369 write("finger <spielername> oder finger <spielername@mudname>\n"+
3370 "Bitte nur den reinen Spielernamen verwenden, keine Namensvorsaetze oder Titel\n");
3371 return 1;
3372 }
3373 xname=explode(str,"@");
3374 if(sizeof(xname)==2)
3375 {
3376 if (xname[0]=="-n " || xname[0]=="-p " || xname[0]=="-s ") {
3377 write("finger <spielername>@<mudname> - der Spielername fehlt.\n");
3378 return 1;
3379 }
3380 if (ret=INETD->_send_udp(xname[1],([
3381 REQUEST: "finger",
3382 SENDER: getuid(ME),
3383 DATA: (explode(xname[0]," ")-({"-n","-p","-s"}))[0]
3384 ]), 1))
3385 write(ret);
3386 else
3387 write("Anfrage abgeschickt.\n");
3388 return 1;
3389 }
3390 "/p/daemon/finger"->finger_single(str,1);
3391 return 1;
3392}
3393
3394string lalign(string str, int wid)
3395{
3396 return (str+" "+
3397 " ")[0..wid-1];
3398}
3399
3400#define MUDS_BAR "\
3401-------------------------------------------------------------------------------"
3402
3403private void format(mixed mud, mixed hosts, string output)
3404{
3405 output += lalign(hosts[mud][HOST_NAME], 20) + " " +
3406 (hosts[mud][HOST_STATUS] ?
3407 hosts[mud][HOST_STATUS] > 0 ?
3408 "UP " + ctime(hosts[mud][HOST_STATUS])[4..15] :
3409 "DOWN " + ctime(-hosts[mud][HOST_STATUS])[4..15]
3410 : "UNKNOWN Never accessed.") + "\n";
3411}
3412
3413static int muds() {
3414 mapping hosts;
3415 int i;
3416 mixed muds, output;
3417
3418 output = lalign("Mudname", 20) + " Status Last access";
3419 output += "\n" + MUDS_BAR[0..sizeof(output)] + "\n";
3420 muds = sort_array(m_indices(hosts = INETD->query("hosts")),#'>);
3421 map(muds, #'format, hosts, &output);
3422 More(output);
3423 return 1;
3424}
3425
3426// **** local property methods
3427static int _set_level(int i)
3428{
3429 if (!intp(i)) return -1;
3430 if (i<1) return -1;
3431 Set(P_LEVEL, i);
3432 GMCP_Char( ([P_LEVEL: i]) );
3433 return i;
3434}
3435
3436static int _set_invis(int a)
3437{
3438 return Set(P_INVIS, intp(a) ? a : !Query(P_INVIS));
3439}
3440
3441/* sets the terminal type */
3442/* note: support vt100 (b/w), ansi (color), dumb (none) */
3443static string _set_tty(string str) {
3444 if(str != "dumb" && str != "vt100" && str != "ansi")
3445 return Query(P_TTY);
3446 return Set(P_TTY, str);
3447}
3448
3449static int stty(string str)
3450{
3451 if(str!="dumb"&&str!="vt100"&&str!="ansi"&&str!="reset")
3452 {
3453 write("Kommando: stty dumb|vt100|ansi oder reset\n");
3454 }
3455 if(str == "reset") {
3456 printf("Dieser Text sollte lesbar sein!\n");
3457 return 1;
3458 }
3459
3460 write("TTY steht jetzt auf "+SetProp(P_TTY,str)+".\n");
3461 if(str == "ansi" || str == "vt100") {
3462 printf("Terminal Test:\n");
3463 printf("VT100: fett unterstrichen "+
3464 "blinkend invers\n");
3465 if(str == "ansi") {
3466 printf("ANSI Farben und VT100 Attribute:\n");
3467 foreach(int fg: 30 .. 37) {
3468 foreach(int bg: 40 .. 47) {
3469 printf("[%d;%dm@", fg, bg);
3470 printf("[%d;%dm@", fg, bg);
3471 printf("[%d;%dm@", fg, bg);
3472 printf("[%d;%dm@", fg, bg);
3473 }
3474 printf("\n");
3475 }
3476 printf("Sollte dieser Text hier nicht richtig lesbar\nsein,"+
3477 "benutze das Kommando stty reset!\n");
3478 }
3479
3480 }
3481 return 1;
3482}
3483
3484int set_ascii_art(string str)
3485{
3486 if (str!="ein"&&str!="aus")
3487 {
3488 printf("Du moechtest 'Grafik' "+(QueryProp(P_NO_ASCII_ART)?"NICHT ":"")+
3489 "sehen.\n");
3490 }
3491
3492 if (str=="ein") {
3493 SetProp(P_NO_ASCII_ART, 0);
3494 printf("Zukuenftig moechtest Du 'Grafik' sehen.\n");
3495 }
3496
3497 if (str=="aus") {
3498 SetProp(P_NO_ASCII_ART, 1);
3499 printf("Zukuenftig moechtest Du KEINE 'Grafik' mehr sehen.\n");
3500 }
3501
3502
3503 return 1;
3504}
3505
3506int _set_shell_version(int arg)
3507{
3508 if (!intp(arg))
3509 return -1;
3510 Set(P_SHELL_VERSION,({QueryProp(P_RACE),arg}));
3511 return 1;
3512}
3513
3514int _query_shell_version()
3515{ mixed sv;
3516
3517 if (!(sv=Query(P_SHELL_VERSION)) || !pointerp(sv) || sizeof(sv)!=2 ||
3518 sv[0]!=QueryProp(P_RACE) || !intp(sv[1]))
3519 return 0;
3520 return sv[1];
3521}
3522
3523// XxXxXxXxXx
3524
3525int more(string str)
3526{
3527 if(!str)
3528 {
3529 notify_fail("Usage: more <file>\n");
3530 return 0;
3531 }
3532 if (file_size(str) <= 0) {
3533 notify_fail(str+": No such file\n");
3534 return 0;
3535 }
3536 More(str, 1);
3537 return 1;
3538}
3539
3540static int set_visualbell(string str)
3541{
3542 if(!str)
3543 {
3544 write("Derzeitige Einstellung fuer Tonausgabe: "+
3545 (QueryProp(P_VISUALBELL)?"AUS":"EIN")+".\n");
3546 return 1;
3547 }
3548 if (str=="ein")
3549 {
3550 if(!QueryProp(P_VISUALBELL))
3551 write("Die Tonausgabe stand schon auf EIN.\n");
3552 else
3553 {
3554 SetProp(P_VISUALBELL,0);
3555 write("OK, Tonausgabe auf EIN gestellt.\n");
3556 }
3557 }
3558 else
3559 if (str=="aus")
3560 {
3561 if(QueryProp(P_VISUALBELL))
3562 write("Die Tonausgabe stand schon auf AUS.\n");
3563 else
3564 {
3565 SetProp(P_VISUALBELL,1);
3566 write("OK, Tonausgabe auf AUS gestellt.\n");
3567 }
3568 }
3569 else
3570 write("Syntax: ton [ein|aus]\n");
3571 return 1;
3572}
3573
3574static int set_screensize(string str)
3575{
3576 int size;
3577
3578 if (str && (str[0..2] == "abs" || str[0..2]=="rel")) {
3579 size = QueryProp(P_MORE_FLAGS);
3580 if (str[0..2] == "abs") {
3581 size |= E_ABS;
3582 write("Es wird beim Prompt die Zeilenzahl des Textes angegeben.\n");
3583 }
3584 else {
3585 size &= ~E_ABS;
3586 write("Es wird beim Prompt der prozentuale Anteil des Textes angegeben.\n");
3587 }
3588 SetProp(P_MORE_FLAGS, size);
3589 return 1;
3590 }
3591
3592 if ( str && (str=="auto" || sscanf( str, "auto %d", size )) ){
3593 if ( size > 0 ){
3594 write("Ungueltiger Wert! "
3595 "In Verbindung mit 'auto' sind nur negative Werte erlaubt.\n");
3596 return 1;
3597 }
3598
3599 SetProp( P_SCREENSIZE, size-1 );
3600
3601 write("Ok, Deine Zeilenzahl wird nun automatisch ermittelt (derzeit "+
3602 QueryProp(P_SCREENSIZE)+").\n"+
3603 break_string("Bitte beachte, dass dies nur einwandfrei "
3604 "funktioniert, wenn Dein Client Telnetnegotiations "
3605 "unterstuetzt (siehe auch \"hilfe telnegs\").") );
3606 return 1;
3607 }
3608
3609 if ( !str || str=="" || !sscanf( str, "%d", size ) || size < 0 || size > 100){
3610 write(break_string(
3611 sprintf("Mit dem Befehl 'zeilen <groesse>' kannst Du einstellen, "
3612 "wieviele Zeilen bei mehrseitigen Texten auf einmal ausgegeben "
3613 "werden. Die angegebene Groesse muss zwischen 0 und 100 liegen. "
3614 "Bei Groesse 0 wird einfach alles ausgegeben (ohne Pause). Mit "
3615 "der Einstellung 'auto' wird die Groesse automatisch ueber "
3616 "die Telnetnegotiations ermittelt (siehe auch 'hilfe telnegs'). "
3617 "Um nach einer Seite Text noch etwas Platz zu haben, kann man z.B. "
3618 "'zeilen auto -3' einstellen.\n"
3619 "Die Voreinstellung ist 20 Zeilen.\n"
3620 "Mit 'zeilen abs[olut]' und 'zeilen rel[ativ]' kannst Du fest"
3621 "legen, ob im Prompt bei langen Texten die aktuelle Zeilennummer "
3622 "oder eine prozentuale Angabe ausgegeben wird.\n"
3623 "Deine aktuelle Einstellung ist %d%s Zeilen (%s).",
3624 QueryProp(P_SCREENSIZE),
3625 Query(P_SCREENSIZE) < 0 ? " 'automatische'" : "",
3626 QueryProp(P_MORE_FLAGS) & E_ABS ? "absolut" : "relativ"),78,0,1));
3627 return 1;
3628 }
3629
3630 SetProp( P_SCREENSIZE, size );
3631
3632 printf( "Okay, Deine Zeilenzahl steht nun auf %d.\n", size );
3633 return 1;
3634}
3635
3636static int _query_screensize()
3637{
3638 int sz,rows;
3639
3640 if ( (sz=Query(P_SCREENSIZE)) >= 0 )
3641 return sz;
3642
3643 if ( !rows=QueryProp(P_TTY_ROWS) )
3644 return 0;
3645
3646 return (rows+=sz) >= 5 ? rows : 5;
3647}
3648
3649static int presay(string str)
3650{
3651 if (!str=_unparsed_args())
3652 write("Dein Presay ist jetzt geloescht.\n");
3653 else
3654 printf("Dein Presay lautet jetzt: \"%s\".\n",str=capitalize(str));
3655 SetProp(P_PRESAY,str);
3656 return 1;
3657}
3658
3659static int sethands(string str)
3660{
3661 mixed *hands;
3662
3663 if (!(str=_unparsed_args()))
3664 {
3665 write("sethands <message>\n");
3666 return 1;
3667 }
3668 if (str=="0")
3669 hands=RaceDefault(P_HANDS);
3670 if (!hands || !pointerp(hands))
3671 hands=Query(P_HANDS);
3672 hands[0]=" "+str;
3673 Set(P_HANDS,hands);
3674 write("Ok.\n");
3675 return 1;
3676}
3677
3678static int inform(string str)
3679{
3680 switch (str) {
3681 case "on":
3682 case "ein":
3683 case "an":
3684 if (Query(P_INFORMME))
3685 write("Das hattest Du schon so eingestellt.\n");
3686 else
3687 {
3688 write("Kuenftig wirst Du informiert, wenn jemand das "MUDNAME" verlaesst/betritt.\n");
3689 Set(P_INFORMME,1);
3690 }
3691 return 1;
3692 case "aus":
3693 case "off":
3694 if (!Query(P_INFORMME))
3695 write("Das hattest Du schon so eingestellt.\n");
3696 else
3697 {
3698 write("Ok.\n");
3699 Set(P_INFORMME,0);
3700 }
3701 return 1;
3702 case 0:
3703 write("Inform-Mode ist "+(Query(P_INFORMME)?"an":"aus")+"geschaltet.\n");
3704 return 1;
3705 }
3706 write("inform an oder inform aus, bitte.\n");
3707 return 1;
3708}
3709
3710void delayed_write(mixed *what)
3711{
3712 if (!pointerp(what)||!sizeof(what)||!pointerp(what[0]))
3713 return;
3714 tell_object(ME,what[0][0]);
3715 if (sizeof(what)>1&&sizeof(what[0])>1)
3716 call_out("delayed_write",what[0][1],what[1..]);
3717}
3718
3719void notify_player_change(string who, int rein, int invis)
3720{
3721 string *list,name;
3722 mixed mlist;
3723
3724 if (invis) name="("+who+")";
3725 else name=who;
3726
3727 if (Query(P_INFORMME))
3728 {
3729 if (rein)
3730 tell_object(ME,name+" ist gerade ins "MUDNAME" gekommen.\n");
3731 else
3732 tell_object(ME,name+" hat gerade das "MUDNAME" verlassen.\n");
3733 }
3734
3735 if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
3736
3737 if(pointerp(list=Query(P_WAITFOR)) && sizeof(list) && member(list,who)!=-1)
3738 {
3739 if (!QueryProp(P_VISUALBELL))
3740 name+=sprintf("%c",7); // Char fuer Pieps an den String anhaengen.
3741 // Moechte der Spieler keine ASCII-Grafik sehen, wird diese Meldung ohne
3742 // Leerzeichen formatiert, so dass sie von Screenreadern vorgelesen wird.
3743 // Anderenfalls wuerde sie einzeln buchstabiert.
3744 if ( QueryProp(P_NO_ASCII_ART) )
3745 {
3746 delayed_write( ({ ({ sprintf("%s IST JETZT %sDA !!!\n",
3747 name, (rein?"":"NICHT MEHR ")) }) }) );
3748 }
3749 else
3750 {
3751 delayed_write( ({ ({ sprintf("%s I S T J E T Z T %sD A !!!\n",
3752 name, (rein?"":"N I C H T M E H R ")) }) }) );
3753 }
3754 }
3755
3756 if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
3757 (mappingp(mlist)) && (mlist[who]))
3758 Show_WaitFor_Reason(who,invis);
3759}
3760
3761static int erwarte(string str)
3762{
3763 string *list,*str1;
3764 mixed mlist;
3765 int i;
3766
3767 if (!mappingp(mlist=QueryProp(P_WAITFOR_REASON)))
3768 mlist=([]);
3769 if (!pointerp(list=Query(P_WAITFOR)))
3770 list=({});
3771
3772 if (!str || str=="-u")
3773 {
3774 if(Query(P_WAITFOR_FLAGS)&0x01)
3775 write("Du hast 'erwarte' temporaer deaktiviert.\n");
3776 write("Du erwartest jetzt");
3777 if (!sizeof(list))
3778 write(" niemanden mehr.\n");
3779 else
3780 {
3781 write(":\n");
3782 if (!str) list=sort_array(list,#'>);
3783 More(break_string(CountUp(list),78));
3784 }
3785 return 1;
3786 }
3787 if(str=="aus"){
3788 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)|0x01);
3789 write("Erwarte ist jetzt deaktiviert.\n");
3790 return 1;
3791 }
3792 if(str=="an" || str=="ein"){
3793 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)&0xFE);
3794 write("Erwarte ist jetzt aktiv.\n");
3795 return 1;
3796 }
3797
3798 str1=explode(_unparsed_args()||""," ");
3799 if (sizeof(str1)==1)
3800 {
3801 if (str1[0]!="wegen")
3802 {
3803 str=capitalize(lower_case(str));
3804 if (member(list,str)!=-1)
3805 {
3806 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,str));
3807 list-=({str});
3808 write(str+" aus der Liste entfernt.\n");
3809 } else
3810 {
3811 if (sizeof(list)>1000)
3812 {
3813 write("Du erwartest schon genuegend!\n");
3814 return 1;
3815 }
3816 list+=({str});
3817 write(str+" an die Liste angehaengt.\n");
3818 }
3819 Set(P_WAITFOR,list);
3820 }
3821 else
3822 {
3823 if (sizeof(mlist) && sizeof(list=m_indices(mlist)))
3824 {
3825 write("Du erwartest aus einem bestimmten Grund:\n");
3826 write(break_string(CountUp(sort_array(list,#'>))+".",78));
3827 }
3828 else write("Du erwartest niemanden aus einem bestimmten Grund.\n");
3829 }
3830 return 1;
3831 }
3832 notify_fail("Falsche Syntax, siehe 'hilfe erwarte'!\n");
3833 if (str1[1]!="wegen") return 0;
3834 if (sizeof(str1)==2)
3835 Show_WaitFor_Reason(capitalize(lower_case(str1[0])),0);
3836 else {
3837 string s=capitalize(lower_case(str1[0]));
3838 if (sizeof(str1)==3 && (str1[2]=="nichts" || str1[2]=="loeschen"))
3839 if (!mlist[s])
3840 write("Du hast "+s+" aus keinem bestimmten Grund erwartet!\n");
3841 else
3842 {
3843 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,s));
3844 write("Du erwartest "+s+" aus keinem bestimmten Grund mehr!\n");
3845 }
3846 else
3847 {
3848 if (IS_ARCH(ME)) i=80; else if (IS_LEARNER(ME)) i=40;
3849 else if (IS_SEER(ME)) i=20; else i=10;
3850 if (!mlist[s] && sizeof(mlist)>=i)
3851 write("Sorry, aber Du erwartest schon genuegend Leute!\n");
3852 else
3853 {
3854 SetProp(P_WAITFOR_REASON,mlist+([s:implode(str1[2..]," ")]));
3855 Show_WaitFor_Reason(s,0);
3856 }
3857 }
3858 }
3859 return 1;
3860}
3861
3862static int uhrmeldung(string str)
3863{
3864 if (!(str=_unparsed_args()))
3865 {
3866 str=QueryProp(P_CLOCKMSG);
3867 if (!str)
3868 {
3869 write("Du hast die Standard-Uhrmeldung.\n");
3870 return 1;
3871 }
3872 if( !stringp(str) ) str = sprintf("%O\n",str);
3873 printf("Deine Uhrmeldung ist:\n%s\n",str[0..<2]);
3874 return 1;
3875 }
3876 if (str=="0")
3877 {
3878 SetProp(P_CLOCKMSG,0);
3879 write("Ok, Du hast jetzt wieder die Standard-Meldung.\n");
3880 return 1;
3881 }
3882 if (sizeof(explode(str,"%d"))>2)
3883 {
3884 write("Fehler, es darf nur ein %d in der Meldung vorkommen.\n");
3885 return 1;
3886 }
3887 /* Mehrere %-Parameter verursachen das Abschalten der Uhr zur vollen Stunde.
3888 */
3889 if (sizeof(explode(str,"%"))>2)
3890 {
3891 write("Fehler: Zuviele %-Parameter in der Meldung.\n");
3892 return 1;
3893 }
3894 /* Nur ein %-Parameter, aber der falsche: nicht sinnvoll. */
3895 else
3896 {
3897 int i = strstr(str,"%",0);
3898 if ( i>-1 && ( i==sizeof(str)-1 || str[i+1]!='d'))
3899 {
3900 write("Fehler: Falscher %-Parameter in der Meldung.\n");
3901 return 1;
3902 }
3903 }
3904 str+="\n";
3905 SetProp(P_CLOCKMSG,str);
3906 write("Ok.\n");
3907 return 1;
3908}
3909
3910static int zeitzone(string str)
3911{
3912 int zt;
3913 if(!str || str==""){
3914 if(!(zt=QueryProp(P_TIMEZONE)))
3915 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
3916 "eingestellt.\n");
3917 else if(zt>0)
3918 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
3919 "eingestellt.\n",zt);
3920 else
3921 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
3922 "Berlin eingestellt.\n",-zt);
3923 return 1;
3924 }
3925 if(sscanf(str,"utc %d",zt)==1) zt=(zt-1)%24;
3926 else zt=to_int(str)%24;
3927
3928 SetProp(P_TIMEZONE,zt);
3929
3930 if(!zt)
3931 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
3932 "eingestellt.\n");
3933 else if(zt>0)
3934 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
3935 "eingestellt.\n",zt);
3936 else
3937 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
3938 "Berlin eingestellt.\n",-zt);
3939 return 1;
3940}
3941
3942static int emailanzeige(string str){
3943 notify_fail("Syntax: emailanzeige [alle|freunde|niemand]\n");
3944 if(!str || str==""){
3945 if(!(str=QueryProp(P_SHOWEMAIL)))str="Niemandem";
3946 else if(str=="alle")str="allen";
3947 else if(str=="freunde")str="Deinen Freunden";
3948 else if(str=="niemand")str="niemandem";
3949 else{
3950 SetProp(P_SHOWEMAIL,0);
3951 str="Niemandem";
3952 }
3953 write("Deine Email wird "+str+" angezeigt.\n");
3954 return 1;
3955 }
3956 else if(member(({"alle","freunde","niemand"}),str)==-1)return 0;
3957
3958 SetProp(P_SHOWEMAIL,str);
3959
3960 if(str=="alle")str="allen";
3961 else if(str=="freunde")str="Deinen Freunden";
3962 else str="niemandem";
3963 write("Deine Email wird "+str+" angezeigt.\n");
3964 return 1;
3965}
3966
3967static int zaubertraenke()
3968{
3969 More("/room/orakel"->TipListe());
3970 return 1;
3971}
3972
3973varargs static int angriffsmeldung(string arg) {
3974 if (arg=="ein" || arg=="an")
3975 SetProp(P_SHOW_ATTACK_MSG,1);
3976 else if (arg=="aus")
3977 SetProp(P_SHOW_ATTACK_MSG,0);
3978 if (QueryProp(P_SHOW_ATTACK_MSG))
3979 write("Du siehst saemtliche Angriffsmeldungen von Dir.\n");
3980 else
3981 write("Du siehst nur neue Angriffsmeldungen von Dir.\n");
3982 return 1;
3983}
3984
3985static mixed _query_localcmds()
3986{
3987 return ({({"zeilen","set_screensize",0,0}),
3988 ({"email","set_email",0,0}),
3989 ({"url","set_homepage",0,0}),
3990 ({"icq","set_icq",0,0}),
3991 ({"messenger", "set_messenger", 0, 0}),
3992 ({"ort","set_location",0,0}),
3993 ({"punkte","short_score",0,0}),
3994 ({"score","short_score",0,0}),
3995 ({"info","score",0,0}),
3996 ({"kurzinfo","very_short_score",0,0}),
3997 ({"quit","new_quit",0,0}),
3998 ({"ende","new_quit",0,0}),
3999 ({"disconnect","disconnect",0,0}),
4000 ({"schlaf","disconnect",1,0}),
4001 ({"speichern","save_character",0,0}),
4002 ({"save","save_character",0,0}),
4003 ({"toete","kill",0,0}),
4004 ({"angriffsmeldung","angriffsmeldung",0,0}),
4005 ({"passw","change_password",1,0}),
4006 ({"hilfe","help",1,0}),
4007 ({"selbstloeschung","self_delete",0,0}),
4008 ({"spielpause","spielpause",0,0}),
4009 ({"spieldauer","spieldauer",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004010 ({"idee","ReportError",0,0}),
4011 ({"typo","ReportError",0,0}),
4012 ({"bug","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004013 ({"fehler","fehlerhilfe",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004014 ({"md","ReportError",0,0}),
4015 ({"detail","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004016 ({"vorsicht","toggle_whimpy",0,0}),
4017 ({"stop","stop",0,0}),
4018 ({"kwho","kwho",0,0}),
4019 ({"kwer","kwho",0,0}),
4020 ({"kkwer","kkwho",0,0}),
4021 ({"kkwho","kkwho",0,0}),
4022 ({"who","who",0,0}),
4023 ({"wer","who",0,0}),
4024 ({"zeit","uhrzeit",0,0}),
4025 ({"uhrzeit","uhrzeit",0,0}),
4026 ({"weg","weg",0,0}),
4027 ({"wegmeldung", "wegmeldung", 0, 0}),
4028 ({"idlezeit", "idlezeit", 0, 0}),
4029 ({"finger","finger",0,0}),
4030 ({"muds","muds",0,0}),
4031 ({"emote","emote",0,0}),
4032 ({":","emote",1,0}),
4033 ({";","emote",1,0}),
4034 ({"remote","remote",0,SEER_LVL}),
4035 ({"r:","remote",1,0}),
4036 ({"r;","gremote",1,0}),
4037 ({"titel","set_title",0,0}),
4038 ({"review","review",0,SEER_LVL}),
4039 ({"setmin","setmin",0,SEER_LVL}),
4040 ({"setmout","setmout",0,SEER_LVL}),
4041 ({"setmmin","setmmin",0,SEER_LVL}),
4042 ({"setmmout","setmmout",0,SEER_LVL}),
4043 ({"sethands","sethands",0,SEER_LVL}),
4044 ({"presay","presay",0,SEER_LVL}),
4045 ({"extralook","extralook",0,SEER_LVL}),
4046 ({"fluchtrichtung","toggle_whimpy_dir",0,SEER_LVL}),
4047 ({"inform","inform",0,0}),
4048 ({"erwarte","erwarte",0,0}),
4049 ({"stty","stty",0,0}),
4050 ({"grafik", "set_ascii_art", 0, 0}),
4051 ({"uhrmeldung","uhrmeldung",0,0}),
4052 ({"zeitzone","zeitzone",0,0}),
4053 ({"behalte","behalte",0,0}),
4054 ({"zweitiemarkierung","zweitiemarkierung",0,0}),
4055 ({"emailanzeige","emailanzeige",0,0}),
4056 ({"topliste","topliste",0,0}),
4057 ({"ton","set_visualbell",0,0}),
4058 ({"telnegs","show_telnegs",0,0}),
4059 ({"spotte", "spotte", 0, 0}),
4060 ({"reise","reise",0,0}),
4061 ({"zaubertraenke","zaubertraenke",0,0}),
4062 ({"telnet","telnet_cmd",0,0}),
4063 })+
4064 command::_query_localcmds()+
4065 viewcmd::_query_localcmds()+
4066 comm::_query_localcmds()+
4067 skills::_query_localcmds()+
4068 description::_query_localcmds();
4069}
4070
4071static int _check_keep(object ob)
4072{
4073 return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
4074}
4075
4076static mixed _set_testplayer(mixed arg) {
4077 mixed res;
4078 object setob;
4079
4080 setob=this_player();
4081 if (!objectp(setob) || !query_once_interactive(setob))
4082 setob=this_interactive();
4083 if (!objectp(setob))
4084 setob=previous_object();
4085 if (setob && !IS_DEPUTY(setob)) {
4086 arg=geteuid(setob);
4087 if (!arg || arg=="NOBODY")
4088 arg=getuid(setob);
4089 arg=capitalize(arg);
4090 }
4091 res=Set(P_TESTPLAYER,arg);
4092 Set(P_TESTPLAYER,PROTECTED,F_MODE_AS);
4093 return res;
4094}
4095
4096int zweitiemarkierung(string arg)
4097{
4098 if (!QueryProp(P_SECOND))
4099 return _notify_fail("Aber Du bist doch gar kein Zweiti.\n"),0;
4100 notify_fail("Syntax: zweitiemarkierung [unsichtbar|sichtbar|name]\n");
4101 if (!arg)
4102 return 0;
4103 switch (arg)
4104 {
4105 case "unsichtbar" :
4106 SetProp(P_SECOND_MARK,-1);
4107 write("Jetzt sieht kein Spieler mehr, dass Du ein Zweiti bist.\n");
4108 return 1;
4109 case "sichtbar" :
4110 SetProp(P_SECOND_MARK,0);
4111 write("Jetzt sieht kein Spieler mehr, wessen Zweiti Du bist.\n");
4112 return 1;
4113 case "name" :
4114 SetProp(P_SECOND_MARK,1);
4115 write("Jetzt koennen alle sehen, wessen Zweiti Du bist.\n");
4116 return 1;
4117 }
4118 return 0;
4119}
4120
4121int topliste(string arg)
4122{
4123 if (!arg)
4124 {
4125 printf("Du hast Dich fuer die Topliste %s.\n",
4126 (QueryProp(P_NO_TOPLIST) ? "gesperrt" : "freigegeben"));
4127 return 1;
4128 }
4129 else if (member(({"j","ja","n","nein"}),arg)==-1)
4130 return _notify_fail("Syntax: topliste [ja|nein]\n"),0;
4131 if (arg[0]=='j')
4132 {
4133 SetProp(P_NO_TOPLIST,0);
4134 write("Du kannst jetzt (theoretisch) in der Topliste auftauchen.\n");
4135 }
4136 else
4137 {
4138 SetProp(P_NO_TOPLIST,1);
Zesstradd2d1982017-01-28 14:03:19 +01004139 "/secure/topliste"->DeletePlayer();
4140 write("Du wirst jetzt nicht (mehr) in den Toplisten auftauchen.\n");
MG Mud User88f12472016-06-24 23:31:02 +02004141 }
4142 Set(P_NO_TOPLIST,SAVE|PROTECTED,F_MODE_AS);
4143 return 1;
4144}
4145
4146int show_telnegs(string arg)
4147{
4148 if (!arg)
4149 {
4150 write("Du bekommst Aenderungen Deiner Fenstergroesse "+
4151 (QueryProp(P_TTY_SHOW)?"":"nicht ")+"angezeigt.\n");
4152 return 1;
4153 }
4154 if (member(({"ein","an","aus"}),arg)==-1)
4155 {
4156 write("Syntax: telnegs [ein|aus]\n");
4157 return 1;
4158 }
4159 if (arg=="ein" || arg=="an")
4160 {
4161 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"":"nun ")+
4162 "Aenderungen Deiner Fenstergroesse angezeigt.\n");
4163 Set(P_TTY_SHOW,1);
4164 return 1;
4165 }
4166 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"nun ":"")+
4167 "Aenderungen Deiner Fenstergroesse nicht "+
4168 (QueryProp(P_TTY_SHOW)?"mehr ":"")+"angezeigt.\n");
4169 Set(P_TTY_SHOW,0);
4170 return 1;
4171}
4172
4173private int set_keep_alive(string str) {
4174 if (str == "ein") {
4175 telnet_tm_counter = 240 / __HEART_BEAT_INTERVAL__;
4176 tell_object(this_object(), break_string(
4177 "An Deinen Client werden jetzt alle 4 Minuten unsichtbare Daten "
4178 "geschickt, um zu verhindern, dass Deine Verbindung zum "MUDNAME
4179 " beendet wird.", 78));
4180 }
4181 else if (str == "aus") {
4182 telnet_tm_counter = 0;
4183 tell_object(this_object(),break_string(
4184 "Du hast das Senden von unsichtbaren Daten (Keep-Alive-Pakete) an "
4185 "Deinen Client ausgeschaltet.",78));
4186 }
4187 else {
4188 if (!telnet_tm_counter)
4189 tell_object(this_object(), break_string(
4190 "An Deinen Client werden keine Keep-Alive-Pakete geschickt.",78));
4191 else
4192 tell_object(this_object(), break_string(
4193 "An Deinen Client werden alle 4 Minuten "
4194 "unsichtbare Daten geschickt, damit Deine Verbindung "
4195 "zum "MUDNAME" nicht beendet wird.",78));
4196 }
4197 return 1;
4198}
4199
4200private int print_telnet_rttime() {
4201 int rtt = QueryProp(P_TELNET_RTTIME);
4202 if (rtt>0)
4203 tell_object(ME, break_string(
4204 "Die letzte gemessene 'round-trip' Zeit vom MG zu Deinem Client "
4205 "und zurueck betrug " + rtt + " us.",78));
4206 else
4207 tell_object(ME, break_string(
4208 "Bislang wurde die 'round-trip' Zeit vom MG zu Deinem Client "
4209 "noch nicht gemessen oder Dein Client unterstuetzt dieses "
4210 "nicht.",78));
4211 return 1;
4212}
4213
4214int telnet_cmd(string str) {
4215 if (!str) return 0;
4216 string *args = explode(str, " ");
4217 string newargs;
4218 if (sizeof(args) > 1)
4219 newargs = implode(args[1..], " ");
4220 else
4221 newargs = "";
4222
4223 switch(args[0])
4224 {
4225 case "keepalive":
4226 return set_keep_alive(newargs);
4227 case "rttime":
4228 return print_telnet_rttime();
4229 }
4230 return 0;
4231}
4232
4233int spotte( string str )
4234{
4235 _notify_fail( "Hier ist nichts, was Du verspotten koenntest!\n" );
4236 return 0;
4237}
4238
4239int behalte(string str)
4240{
4241 object ob,*obs;
4242 string s;
4243
4244 if (str)
4245 {
4246 if (str=="alles") {
4247 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, getuid());
4248 write("Ok!\n");
4249 return 1;
4250 }
4251 if (str=="nichts") {
4252 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, 0);
4253 write("Ok!\n");
4254 return 1;
4255 }
4256 if (!sizeof(obs=find_obs(str,PUT_GET_NONE)))
4257 {
4258 _notify_fail("Aber sowas hast Du nicht dabei!\n");
4259 return 0;
4260 }
4261 else ob=obs[0];
4262
4263 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4264 ob->SetProp(P_KEEP_ON_SELL,0);
4265 else
4266 ob->SetProp(P_KEEP_ON_SELL,geteuid(ME));
4267
4268 // erneut abfragen, da sich der Wert nicht geaendert haben muss
4269 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4270 write(break_string(sprintf("Ok, Du wirst %s jetzt bei 'verkaufe alles' "
4271 "behalten.\n",ob->name(WEN)),78));
4272 else
4273 write(break_string(sprintf("Ok, Du wirst %s beim naechsten 'verkaufe "
4274 "alles' mitverkaufen!\n",ob->name(WEN)),78));
4275
4276 return 1;
4277 }
4278 s=make_invlist(ME,filter(all_inventory(ME),#'_check_keep)); //'));
4279 More(s);
4280 return 1;
4281}
4282
4283static int _query_lep()
4284{
4285 int val;
4286 val = LEPMASTER->QueryLEP();
4287 Set( P_LEP, val );
4288 return val;
4289}
4290
4291static mixed _set_fraternitasdonoarchmagorum(mixed arg)
4292{
4293 if (!intp(arg)) return -1;
4294
4295 if ((!previous_object(1)||object_name(previous_object(1))!=FAO_MASTER) &&
4296 (!this_interactive() || !IS_ARCH(this_interactive())))
4297 return -1;
4298
4299 if (!intp(arg)) return -1;
4300
4301 log_file("fao/P_FAO",sprintf("%s - %s P_FAO gesetzt auf %O\n",
4302 dtime(time()),query_real_name(),arg) );
4303 return Set(P_FAO,arg);
4304}
4305
4306nomask void set_realip(string str)
4307{
4308 if(previous_object() && strstr(object_name(previous_object()),"/secure")==0)
4309 {
4310 realip=str;
4311 }
4312}
4313
4314nomask string query_realip()
4315{
4316 return realip ? realip : "";
4317}
4318
4319mixed _query_netdead_env() {
4320 return ndead_location || ndead_l_filename;
4321}