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