blob: bf332b7d41c706f5e56378c34ce8aa97f472f342 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// player/base.c -- the basic player object
4//
5// $Id: base.c 9467 2016-02-19 19:48:24Z Zesstra $
6#pragma strong_types
7#pragma save_types
8#pragma range_check
9#pragma no_clone
10#pragma pedantic
11
12#include <sys_debug.h>
13#include <regexp.h>
14#include <input_to.h>
15#include <logging.h>
16#include <werliste.h>
17#include <time.h>
18#include <errord.h>
19#include <wizlevels.h>
20#include <money.h>
21
22inherit "/std/hook_provider";
23inherit "/std/player/restrictions";
24inherit "/std/living/attributes";
25inherit "/std/living/put_and_get";
26inherit "/std/living/clothing";
27inherit "/std/thing/properties";
28inherit "/std/player/util";
29inherit "/std/thing/language";
30inherit "/std/player/travel";
31inherit "/std/player/combat";
32inherit "/std/player/description";
33inherit "/std/player/moving";
34inherit "/std/player/life";
35inherit "/std/player/comm";
36inherit "/std/player/viewcmd";
37inherit "/std/player/moneyhandler";
38inherit "/std/player/command";
39inherit "/std/living/skill_attributes";
40inherit "/std/living/light";
41inherit "/std/player/skills";
42inherit "/std/player/quests";
43inherit "/std/player/potion";
44inherit "/std/player/soul";
45inherit "/std/more";
46inherit "/std/user_filter";
47inherit "/secure/telnetneg";
48inherit "/std/player/guide";
49inherit "/std/player/reputation";
50inherit "/std/player/protocols/gmcp";
51inherit "/std/living/helpers";
52
53#define NEED_PROTOTYPES
54#include <player/skills.h>
55#include <player/gmcp.h>
Zesstraa64deb12016-08-31 22:22:07 +020056#include <player/base.h>
MG Mud User88f12472016-06-24 23:31:02 +020057#undef NEED_PROTOTYPES
58#include <player.h>
59#include <properties.h>
60#include <udp.h>
61#include <config.h>
62#include <ansi.h>
63#include <wizlevels.h>
64#include <living.h>
65#include <attributes.h>
66#include <language.h>
67#include <moving.h>
68#include <defines.h>
69#include <terminal.h>
70#include <new_skills.h>
71#include <pager.h>
72#include <combat.h>
73#include "/secure/questmaster.h"
74#include "/secure/lepmaster.h"
75#include <events.h>
76
77#undef NAME /* DEFINED BY UDP.H; BAD NAME CLASH :( */
78#define NAME(who) capitalize(getuid(who))
79
80mapping autoload; /* autoload-mapping */
81int hc_play;
82
83private nosave mapping autoload_rest;
84private nosave string *autoload_error;
85private nosave string realip;
86
87private nosave string passw; /* temporarily for password change */
88private nosave string passwold; /* temporarily for password change */
89
90// HB-Zaehler. Wenn 0 erreicht wird, wird ein Telnet TM Paket als Keep-Alive
91// an den Client gesendet und der Counter wieder auf hochgesetzt.
92// Wenn == 0, ist das Keep-Alive abgeschaltet.
93private int telnet_tm_counter;
94
95nosave string default_home; /* Where to move us if we dont have a home set */
96
97nosave int ndead_lasttime;
98nosave mixed ndead_location;
99nosave string ndead_l_filename;
100nosave int ndead_currently;
101nosave int ndead_next_check;
102nosave object *hb_obs;
103
104private nosave string default_pray_room;
105
106nosave mixed env_ndead_info;
107
108static int _set_invis(int a);
109static string _set_tty(string str);
110static mixed _set_fraternitasdonoarchmagorum(mixed arg);
111
112
113static string al_to_title(int a);
114
115static void ndead_revive();
116
117static int wegmeldung(string player);
118
119int quit();
120void save_me(mixed value_items);
121varargs int remove(mixed arg);
122mixed RaceDefault(string arg);
123
124/** Setzt Defaultwerte vor dem Laden des Savefiles.
125 Nur die Werte bleiben spaeter uebrig, die NICHT aus dem Savefile geladen
126 werden und diese hier ersetzen.
127 Ausserdem kann man hier nicht mit den Werten aus den Savefiles arbeiten.
128 Hierzu muss man updates_after_restore() verwenden.
129*/
130protected void create()
131{
132 if(QueryProp(P_LEVEL))
133 {
134 return; // darf nur EINMAL gemacht werden
135 }
136 call_out("checkConsistency", 0);
137
138 ndead_next_check=NETDEAD_CHECK_TIME;
139 ndead_lasttime=0;
140 ndead_location=0;
141 ndead_l_filename=0;
142 ndead_currently=0;
143 ndead_next_check=0;
144 hc_play=0;
145
146 command::create();
147 properties::create();
148 description::create();
149 light::create();
150 attributes::create();
151 clothing::create();
152 combat::create();
153 life::create();
154 comm::create();
155 viewcmd::create();
156 quests::create();
157 restrictions::create();
158 moving::create();
159 travel::create();
160 skills::create();
161
162 SetProp(P_LEVEL, -1);
163 Set(P_LEVEL, SAVE|SECURED, F_MODE_AS);
164 Set(P_GHOST, SAVE, F_MODE_AS);
165 SetProp(P_SCREENSIZE, -1);
166 Set(P_SCREENSIZE, SAVE,F_MODE_AS);
167 Set(P_MORE_FLAGS, SAVE, F_MODE_AS);
168 SetProp(P_WEIGHT_PERCENT,100);
169 SetProp(P_LONG, 0);
170 SetProp(P_TITLE, "der hoffnungsvolle Anfaenger");
171 SetProp(P_ALIGN, 0);
172 SetProp(P_GENDER, NEUTER);
173 Set(P_GENDER, SAVE, F_MODE_AS);
174 SetProp(P_TTY, "vt100");
175 Set(P_TTY, SAVE, F_MODE_AS);
176 SetProp(P_WEIGHT, 75000);
177 SetProp(P_MAX_HP,50);
178 SetProp(P_MAX_SP,50);
179 SetProp(P_MAX_FOOD,100);
180 SetProp(P_MAX_DRINK,100);
181 SetProp(P_MAX_ALCOHOL,100);
182 Set( P_WIMPY, 20, F_VALUE);
183
184 SetProp(P_HANDS, ({" mit blossen Haenden", 30}));
185 Set(P_HANDS, SAVE, F_MODE_AS);
186 SetProp(P_MAX_HANDS, 2);
187
188 Set(P_MARRIED, SAVE, F_MODE_AS);
189 Set(P_EXTRA_LOOK, SAVE, F_MODE_AS);
190 Set(P_SHOW_EXITS, SAVE, F_MODE_AS);
191 Set(P_SHOW_EXITS, 1);
192 Set(P_WANTS_TO_LEARN, SAVE, F_MODE_AS);
193 Set(P_CAN_FLAGS, SAVE, F_MODE_AS);
194 Set(P_TESTPLAYER, SAVE|PROTECTED, F_MODE_AS);
195 Set(P_ALLOWED_SHADOW, SAVE|SECURED, F_MODE_AS);
196 Set(P_SECOND, SAVE, F_MODE_AS);
197 Set(P_INVIS, SAVE, F_MODE_AS);
198 Set(P_READ_NEWS, SAVE, F_MODE_AS);
199 Set(P_START_HOME, SAVE, F_MODE_AS);
200 Set(P_PRAY_ROOM, SAVE, F_MODE_AS);
201 Set(P_MAILADDR, SAVE, F_MODE_AS);
202 Set(P_HOMEPAGE, SAVE, F_MODE_AS);
203 Set(P_ICQ, SAVE, F_MODE_AS);
204 Set(P_MESSENGER, SAVE, F_MODE_AS);
205 Set(P_LOCATION, SAVE, F_MODE_AS);
206
207 Set(P_NO_ASCII_ART, SAVE, F_MODE_AS);
208
209 Set(P_VISUALBELL, SAVE, F_MODE_AS);
210 Set(P_CARRIED_VALUE, SAVE, F_MODE_AS);
211
212 Set(P_PROMPT, "> ");
213 Set(P_PROMPT, SAVE, F_MODE_AS);
214 Set(P_CALLED_FROM_IP, SAVE, F_MODE_AS);
215 Set(P_INFORMME,SAVE|PROTECTED,F_MODE_AS);
216 Set(P_WAITFOR,SAVE|PROTECTED,F_MODE_AS);
217 Set(P_WAITFOR_REASON,SAVE|PROTECTED,F_MODE_AS);
218 Set(P_DAILY_PLAYTIME,SAVE|PROTECTED,F_MODE_AS);
219 Set(P_NETDEAD_ENV, PROTECTED|NOSETMETHOD, F_MODE_AS);
220
221 autoload = ([]);
222 autoload_rest = ([]);
223 autoload_error = ({});
224 SetProp(P_ARTICLE,0);
225 Set(P_GUILD,SAVE,F_MODE_AS);
226 Set(P_GUILD_TITLE,SAVE,F_MODE_AS);
227 Set(P_GUILD_LEVEL,SAVE,F_MODE_AS);
228 Set(P_GUILD_RATING,SAVE,F_MODE_AS);
229 Set(P_NEWSKILLS,SAVE,F_MODE_AS);
230 Set(P_NEEDED_QP,REQ_QP);
231 Set(P_DEADS,0);
232 Set(P_DEADS, NOSETMETHOD,F_SET_METHOD);
233 Set(P_DEADS,SAVE|PROTECTED|SECURED,F_MODE_AS);
234 Set(P_LAST_LOGIN,-1);
235 Set(P_LAST_LOGIN, NOSETMETHOD,F_SET_METHOD);
236 Set(P_LAST_LOGIN,SAVE|PROTECTED|SECURED,F_MODE_AS);
237 Set(P_LAST_LOGOUT,-1);
238 Set(P_LAST_LOGOUT, NOSETMETHOD,F_SET_METHOD);
239 Set(P_LAST_LOGOUT,SAVE|PROTECTED|SECURED,F_MODE_AS);
240 Set(P_CLOCKMSG,SAVE,F_MODE_AS);
241 Set(P_TIMEZONE,SAVE,F_MODE_AS);
242 Set(P_SHOWEMAIL,SAVE,F_MODE_AS);
243 Set(P_LAST_QUIT,SAVE|PROTECTED|SECURED,F_MODE_AS);
244
245 Set(P_CMSG, 0, F_MODE); // to clean out the old clone messages
246 Set(P_DMSG, 0, F_MODE);
247 Set(P_CLONE_MSG, SAVE, F_MODE);
248 SetProp(P_CLONE_MSG, "zaubert etwas hervor");
249 Set(P_DESTRUCT_MSG, SAVE, F_MODE);
250 SetProp(P_DESTRUCT_MSG, "verschwindet einfach");
251
252 Set(P_FAO, SAVE|SECURED, F_MODE_AS);
253 Set(P_FAO_PORTALS, SAVE, F_MODE_AS);
254
255 SetProp(P_NEWBIE_GUIDE,0);
256 Set(P_NEWBIE_GUIDE,SAVE,F_MODE_AS);
257
258 //TODO: Remove - Property ist not needed any more.
259 Set(P_TELNET_KEEP_ALIVE, PROTECTED|SAVE, F_MODE_AD);
260 SetProp(P_TELNET_KEEP_ALIVE, 0);
261
262 AddId("Interactive");
263
264 realip="";
265}
266
267// ACHTUNG: Falls hier mal sonst noch weitere Resets geerbt werden, muss das
268// hier ordentlich definiert werden!
269void reset() {
270 comm::reset();
271 // momentan kann der Reset jetzt abgeschaltet werden, da er nur die
272 // TM-History loescht und ggf. von Netdead() und Disconnect() reaktiviert
273 // wird.
274 set_next_reset(-1);
275}
276
277protected void NotifyMove(object dest, object oldenv, int method)
278{
279 moving::NotifyMove(dest,oldenv,method);
280 // ggf. Daten ueber neues Env per GMCP senden.
281 if (dest != oldenv)
282 GMCP_Room();
283}
284
285string Forschung()
286{
287 return LEPMASTER->QueryForschung();
288}
289
290/** Setzt Defaultwerte fuer Rassen - wird von den Shells ueberschrieben.
291*/
292mixed RaceDefault(string arg)
293{
294 if (!arg)
295 return 0;
296 switch(arg)
297 {
298 case P_HANDS :
299 return ({" mit blossen Haenden",30,DT_BLUDGEON});
300 case P_BODY :
301 return 0;
302 }
303 return 0;
304}
305
306/** Prueft Spielerobjekt auf Konsistenz.
307 Ueberprueft das Spielerobjekt nach kompletter Initialisierung (im
308 callout) auf korrekte Werte einiger Props.
309*/
310void checkConsistency()
311{ mixed h;
312 int m;
313
314 if (pointerp(h=RaceDefault(P_HANDS)) && sizeof(h)>1)
315 m=(int)h[1];
316 else
317 m=30;
318 if((h=Query(P_HANDS))[1] > m && !IS_LEARNER(this_object())) {
319 log_file("inconsistent", sprintf(
320 "[%s] %O: HANDS: %d\n", dtime(time()), this_player(), h[1]));
321 h[1] = m;
322 Set(P_HANDS,h);
323 }
324
325 if (Query(P_BODY)!=(m=RaceDefault(P_BODY)))
326 Set(P_BODY,m);
327
328 if (!Query(P_SECOND_MARK,F_MODE))
329 Set(P_SECOND_MARK,SAVE|PROTECTED,F_MODE_AS);
330 if (!Query(P_TTY_SHOW,F_MODE)&SAVE)
331 {
332 Set(P_TTY_SHOW,0,F_VALUE);
333 Set(P_TTY_SHOW,SAVE,F_MODE_AS);
334 }
335 if (Query(P_TTY_COLS,F_MODE)&SAVE)
336 {
337 Set(P_TTY_COLS,SAVE,F_MODE_AD);
338 Set(P_TTY_ROWS,SAVE,F_MODE_AD);
339 Set(P_TTY_TYPE,SAVE,F_MODE_AD);
340 }
341}
342
343/** Bittet das Spielerobjekt, sich zu zerstoeren.
344 \param[in] silent Flag, ob ohne Textausgaben zerstoert werden soll
345 \return Erfolg der Selbstzerstoerung
346*/
347varargs int remove(int silent)
348{
349 return moving::remove(silent);
350}
351
352/** Schaltet in allen Objekten im Inv HBs aus.
353 Schaltet im Inv in allen Objekten (rekursiv, deep_inventory)
354 die Heartbeats aus. Falls obs uebergeben wird, werden diese Objekte
355 statt des Inventars benutzt.
356 Speichert die Objekte, die einen HB hatten, in der glob. Var. hb_obs.
357 @param[in] obs mixed - Objekte, in denen der HB abgeschaltet werden soll.
358 Container werden rekursiv behandelt.
359 @attention Achtung! Niemals zweimal hintereinander rufen, ohne
360 zwischendurch restart_heart_beats() gerufen zu haben!
361 @sa restart_heart_beats
362*/
363varargs static void stop_heart_beats(mixed obs)
364{
365 int i;
366
367 if (!obs)
368 {
369 hb_obs=({});
370 obs=deep_inventory(ME);
371 }
372 foreach(mixed ob: obs) {
373 if (pointerp(ob))
374 stop_heart_beats(ob);
375 else if (set_object_heart_beat(ob,0))
376 hb_obs+=({ob});
377 }
378}
379
380/** Schaltet HBs in Objekten im Inv wieder ein.
381 Schaltet in allen Objekten in hb_obs den Heartbeat ein.
382 In hb_obs (glob. Var.) stehen alle Objekte, die beim letzten Aufruf von
383 stop_heart_beats() einen HB hatten.
384 @sa stop_heart_beats
385*/
386static void restart_heart_beats()
387{
388 int i;
389
390 if (pointerp(hb_obs))
391 {
392 foreach(object ob: hb_obs)
393 set_object_heart_beat(ob,1);
394 hb_obs=0;
395 }
396}
397
398/** Prueft auf abgelaufene Spielzeit.
399 Prueft in Spielerobjekten, ob die taegliche Maximalspielzeit
400 abgelaufen ist.
401 Wird im heart_beat() gerufen.
402 @return int - Flag, ob Spielzeit abgelaufen und Logout erfolgen soll
403*/
404static int CheckDailyPlaytime() {
405 int *spieldauer,d;
406
407 if (!pointerp(spieldauer=Query(P_DAILY_PLAYTIME)))
408 return 0;
409 // 0:Minuten pro Tag, 1:Nr. des letzen Tages, 2:Nr. des angefangenen Tages,
410 // 3:letzte Zeitpruefung, 4:verbleibende Zeit
411 d=time()/86400;
412 if (spieldauer[1]<=d) { // Ende der zeitbeschraenkten Tage?
413 Set(P_DAILY_PLAYTIME,0);
414 return 0;
415 } else if (spieldauer[2]!=d) { // Neuer Tag?
416 spieldauer[4]=spieldauer[0];
417 spieldauer[2]=d;
418 } else {
419 spieldauer[4]-=(time()-spieldauer[3]);
420 }
421 spieldauer[3]=time(); // Letzte Zeitpruefung
422 Set(P_DAILY_PLAYTIME,spieldauer);
423 if (spieldauer[4]<0) { // Keine Zeit mehr uebrig fuer heute
424 if (!interactive(ME))
425 return 1;
426 write("Du hast lange genug gemuddet fuer heute.\n");
427 say(Name(WER)+" hat fuer heute genug gemuddet.\n");
428 remove_interactive(ME);
429 return 1;
430 }
431 return 0;
432}
433
434/** Gibt Erwartemeldung mit Grund aus.
435 @param[in] who string - Wer ist hereingekommen?
436 @param[in] invis int - Ist der Spieler Invis?
437*/
438static void Show_WaitFor_Reason(string who, int invis)
439{
440 mixed list;
441 string reason,name;
442
443 if (invis) name="("+who+")";
444 else name=who;
445 if ((mappingp(list=QueryProp(P_WAITFOR_REASON))) && (reason=list[who]))
446 tell_object(ME,sprintf("\nDu erwartest %s wegen:\n%s\n",name,reason));
447 else
448 tell_object(ME,sprintf("Du erwartest %s aus keinem bestimmten Grund.\n",
449 name));
450}
451
452/** Gibt Liste der Erwarteten Spieler an this_player() aus.
453*/
454static void ListAwaited() //Anwesende Erwartete auflisten
455{
456 string *list;
457 mixed mlist;
458 object ob;
459 int mag;
460
461 mag=IS_LEARNER(ME);
462
463 list=({});
464 foreach(string erwartet : QueryProp(P_WAITFOR)) {
465 if (objectp(ob=find_player(lower_case(erwartet)))) {
466 if (ob->QueryProp(P_INVIS)) {
467 if (mag) list+=({ sprintf("(%s)",erwartet) });
468 }
469 else list+=({erwartet});
470 }
471 }
472 if (sizeof(list))
473 printf("Anwesende Erwartete: %s.\n",
474 CountUp(sort_array(list,#'>)));
475
476 if ((mappingp(mlist=QueryProp(P_WAITFOR_REASON))) && (sizeof(mlist)))
477 {
478 foreach(string erwartet : mlist) {
479 if (!(ob=find_player(lower_case(erwartet))) ||
480 (!mag && ob->QueryProp(P_INVIS)));
481 else Show_WaitFor_Reason(erwartet,ob->QueryProp(P_INVIS));
482 }
483 }
484}
485/** Teilt den Gilden und anderen Spielern mit, wer reingekommen ist.
486 Ausserdem wird ggf. PlayerQuit() im Environment gerufen.
487 \param[in] rein int - wahr, wenn der Spieler einloggt.
488*/
489protected void call_notify_player_change(int rein)
490{
491 string wer = getuid(ME);
492 // erst die Gilde informieren
493 string gilde = QueryProp(P_GUILD);
494 if (stringp(gilde) && (find_object("/gilden/"+gilde)
495 || file_size("/gilden/"+gilde+".c")>0))
496 catch(("/gilden/"+gilde)->notify_player_change(ME, rein); publish);
497
498 // dann die anderen Spieler
499 int mag = IS_LEARNER(ME);
500 int invis = QueryProp(P_INVIS);
501 object *u = users() - ({ME}); // sich selber nicht melden
502 if (mag && invis) { // Invismagier nur Magiern melden
503 u = filter(u, function int (object o)
504 { return query_wiz_level(o) >= LEARNER_LVL; }
505 );
506 }
507 u->notify_player_change(capitalize(wer),rein,invis);
508
509 // und beim Ausloggen noch das Env informieren.
510 if (!rein) {
511 if(environment()) catch(environment()->PlayerQuit(ME);publish);
512 }
513}
514
515/** Ruft im uebergebenen Objekt ein init() auf, sofern notwendig.
516 Ruft in ob ein init() auf, falls das Objekt nach dem
517 letzten Ausloggen geschaffen wurde.
518 \param[in] ob object - Objekt, in dem init() gerufen wird.
519 \param[in] logout int - Letzter Logout
520 \return 1, falls init() gerufen wurde. 0 sonst.
521*/
522static int call_init( object ob, int logout )
523{
524 if ( objectp(ob) && object_time(ob) > logout )
525 return catch(ob->init(); publish), 1;
526 return(0);
527}
528
529/** Holt seit dem letzten Ausloggen ausgefallene Inits nach.
530 Ruft in den uebergebenen Objekten call_init(), was einen init()
531 ausloest, falls das Objekt seit dem letzten Ausloggen erstellt wurde.
532 \param[in] logout Zeitpunkt des letzten Logouts
533 \param[in] obs Array von Objekten
534 \sa call_init()
535*/
536static void inits_nachholen( int logout, object *obs )
537{
538 filter( obs, "call_init", ME, logout );
539}
540
541/** Belebt einen Netztoten wieder.
542 Wird im Login gerufen, wenn der Spieler netztot war. Aequivalent zu
543 start_player()
544 @param[in] silent Wenn Flag gesetzt, werden keine Meldung an den Raum
545 ausgegeben.
546 @param[in] ip Textuelle Repraesentation der IP-Adresse, von der der Spieler
547 kommt.
548 @see start_player()
549*/
550varargs void Reconnect( int silent )
551{
552 int num;
553 string called_from_ip;
554 object *inv;
555
556 if ( query_once_interactive(ME) )
557 {
558 // perform the telnet negotiations. (all that are available)
559 "*"::startup_telnet_negs();
560 Set( P_LAST_LOGIN, time() );
561 }
562
563 enable_commands();
564 set_living_name( getuid() );
565 _remove_netdead();
566 set_heart_beat(1);
567 // Hunttimes aktualisieren und ggf. Feinde vergessen.
568 update_hunt_times((time()-QueryProp(P_LAST_LOGOUT)) /__HEART_BEAT_INTERVAL__);
569 // Heartbeats in Objekten im Inv reaktiveren.
570 restart_heart_beats();
571 // life.c will ggf. was aufraeumen
572 life::reconnect();
573
Zesstrae88826c2016-09-24 20:37:05 +0200574 log_file( "syslog/shell/REENTER", sprintf( "%-11s %s, %-15s (%s).\n",
MG Mud User88f12472016-06-24 23:31:02 +0200575 capitalize(getuid(ME)), ctime(time())[4..15],
576 query_ip_number(ME)||"Unknown",
577 query_ip_name(ME)||"Unknown" ),
578 200000 );
579
580 if ( ndead_currently )
581 ndead_revive();
582
583 if ( !silent && interactive(ME) )
584 call_notify_player_change(1);
585
586 command::reconnect();
587 if ( query_once_interactive(ME) )
588 modify_prompt();
589
590 // Login-event ausloesen
591 EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
592 E_OBJECT: ME,
593 E_PLNAME: getuid(ME),
594 E_ENVIRONMENT: environment() ]) );
595
596 catch( num = "secure/mailer"->FingerMail(geteuid());publish );
597
598 if ( num )
599 write( "Du hast " + num + " neue" + (num == 1 ? "n Brief" : " Briefe")
600 + " im Postamt liegen.\n" );
601
602 if ( QueryProp(P_AWAY) )
603 write( break_string( "Du bist als abwesend gekennzeichnet: " +
604 QueryProp(P_AWAY) + ".", 78 ) );
605
606 catch( RegisterChannels(); publish );
607
608 if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
609 query_ip_number(ME) != called_from_ip ) {
610 string tmp;
611
612 if ( stringp(tmp = query_ip_name(called_from_ip)) &&
613 tmp != called_from_ip )
614 tmp = " [" + tmp + "]";
615 else
616 tmp = "";
617
618 write( "Das letzte Mal kamst Du von " + called_from_ip + tmp + ".\n" );
619 }
620
621 Set( P_CALLED_FROM_IP, query_ip_number(ME) );
622
623 // falls Gegenstaende mit 'upd -ar' upgedated wurden, muessen die
624 // "verloren gegangenen" init()'s nachgeholt werden
625 if ( query_once_interactive(ME) && !IS_LEARNER(ME) )
626 call_out( "inits_nachholen", 0, Query(P_LAST_LOGOUT),
627 all_inventory(ME) );
628
629 // noch nicht geclonte Autoloader "nach"clonen
630 while ( remove_call_out("load_auto_objects") != -1 )
631 /* do nothing */;
632
633 if ( sizeof(autoload_rest) )
634 call_out( "load_auto_objects", 0, autoload_rest );
635
636 if (ndead_location) {
637 catch( ndead_location->BecomesNetAlive(ME);publish );
638 inv = all_inventory(ndead_location);
639 ndead_location = 0;
640 }
641 else
642 inv = ({});
643
644 inv += deep_inventory(ME);
645
646 //ZZ foreach statt call_other(), damit nen bug in BNA nicht die anderen
647 //BNA verhindert.
648 foreach(object ob: inv) {
649 //es ist nicht auszuschliessen, dass Items durch BecomesNetAlive()
650 //eines anderen zerstoert werden.
651 if (objectp(ob))
652 catch( call_other(ob, "BecomesNetAlive", ME);publish );
653 }
654
655 // Erst an dieser Stelle, weil der Spieler u.U. durch ein BecomesNetAlive()
656 // noch bewegt wurde.
657 if ( !silent && environment() && object_name(environment()) != NETDEAD_ROOM )
658 {
659 if(query_hc_play()<=1)
660 tell_room(environment(),QueryProp(P_NAME) + " weilt wieder unter den Lebenden.\n",({ME}) );
661 else
662 tell_room(environment(),QueryProp(P_NAME) + " weilt wieder unter den Verstorbenen.\n",({ME}) );
663 }
664
665 NewbieIntroMsg();
666
667 if ( query_once_interactive(ME) )
668 ListAwaited();
669}
670
671/** Loggt einen Spieler aus und macht ihn netztot.
672 Bewegt einen Spieler in den Netztotenraum, deaktiviert Heartbeats im
673 Inventar, ruft BecomesNetDead(), loest Erwartemeldungen aus, triggert
674 Ausloggevent.
675*/
676void NetDead()
677{
678 object *inv;
679 int num;
680
681 catch(RemoveChannels();publish);
682
683 if(query_hc_play()>1)
684 say("Ploetzlich weicht alle spirituelle Energie aus "+QueryProp(P_NAME)+".\n");
685 else
686 say("Ploetzlich weicht alles Leben aus "+QueryProp(P_NAME)+".\n");
687
688 _set_netdead();
689 remove_call_out("quit");
690 remove_living_name();
691 // Wird zwar im save_me() gemacht, aber die Zeitunterschiede beim
692 // fingern direkt nach dem Ausloggen fuehren immer wieder zu Verwirrungen
693 if(query_once_interactive(ME) && !QueryProp(P_INVIS))
694 Set(P_LAST_LOGOUT,time());
695 if (ME)
696 ndead_location = environment();
697
698 if (query_once_interactive(ME))
699 call_notify_player_change(0);
700
701 // Logout-event ausloesen
702 EVENTD->TriggerEvent(EVT_LIB_LOGOUT, ([
703 E_OBJECT: ME,
704 E_PLNAME: getuid(ME),
705 E_ENVIRONMENT: environment() ]) );
706
707 set_next_reset(900);
708 /* Bei Nicht-Magier-Shells wird comm::reset() aufgerufen, das prueft, ob
709 der Spieler immer noch netztot ist, und falls ja, die tmhist loescht.
710 Die Methode wird von /std/shells/magier.c ueberschrieben, netztote
711 Magier (die eigentlich schon anderweitig beseitigt worden sein sollten)
712 werden remove()d und destruct()ed. --Amynthor 05.05.2008 */
713
714 if (environment()) {
715 catch(environment()->BecomesNetDead(ME);publish);
716 inv = deep_inventory(ME)+all_inventory(environment());
717 }
718 else inv=deep_inventory(ME);
719 foreach(object ob: inv) {
720 if (objectp(ob)) //man weiss nie was BND() macht...
721 catch( call_other(ob, "BecomesNetDead", ME);publish );
722 }
723}
724
725
726/** Sendet ggf. Telnet Timing Marks als Keep-Alive Pakete an den Client.
727 * Wird in heart_beat() gerufen.
728 * @return 1, falls der Spieler Keep-Alive Paket wuenscht, sonst 0.
729 * @see heart_beat()
730*/
731protected int CheckTelnetKeepAlive() {
732 if (telnet_tm_counter > 0) {
733 // Spieler hat offenbar ein Keep-Alive konfiguriert ...
734 if (!(--telnet_tm_counter)) {
735 // und das Intervall ist gerade abgelaufen.
736 // Prop offenbar gesetzt, FEature ist wirklich gewuenscht,
737 // Telnet Timing Mark senden
738 send_telnet_timing_mark();
739 // alle 120 HBs (240s, 4min).
740 // sollte eigentlich 240 / __HEART_BEAT_INTERVAL__ sein. Aber spart
741 // eine Operation im HB. ;-)
742 telnet_tm_counter = 120;
743 }
744 return 1; // Keep-Alive ist eingeschaltet
745 }
746 return 0;
747}
748
749static void ndead_move_me();
750
751/** Heartbeat des Spielerobjektes.
752 Prueft taegliche Spielzeit, speichert regelmaessig den Spieler,
753 bewegt Netztote in den Netztodenraum, ruft die HBs aus living/combat und
754 player/life.
755*/
756protected void heart_beat() {
757 if (!ME)
758 return;
759 if (ndead_currently)
760 {
761 if (interactive(ME))
762 {
763 ndead_revive();
764 ndead_location=0;
765 }
766 else return;
767 }
768 else
769 if (!(ndead_next_check--))
770 {
771 ndead_next_check=NETDEAD_CHECK_TIME;
772 if (!interactive(ME))
773 if (ndead_lasttime)
774 {
775 save_me(1);
776 if (IS_LEARNER(ME))
777 {
778 quit();
779 if (ME)
780 remove();
781 if (ME)
782 destruct(ME);
783 return;
784 }
785 ndead_move_me();
786 // Zumindest bei Gaesten ist das Objekt jetzt zerstoert
787 if (!objectp(this_object()))
788 return;
789 }
790 else
791 ndead_lasttime=1;
792 }
793 if (ME && ndead_lasttime && interactive(ME))
794 ndead_lasttime=0;
795 if (CheckDailyPlaytime())
796 return;
797
798 CheckTelnetKeepAlive();
799
800 life::heart_beat();
801 combat::heart_beat();
802 skills::heart_beat();
803}
804
805/** ID-Funktion fuer Spielerobjekte.
806 * id() fuer Spieler. Besondere Behandlung fuer Froesche und Geister,
807 * sowie Invis-Status.
808 * Gibt immer 0 zurueck, wenn P_INVIS && lvl < P_LEVEL
809 * @param[in] str angefragter ID-String
810 * @param[in] lvl Level des Anfragenden
811 * @return 1, falls str auf den Spieler zutrifft, 0 sonst.
812 */
813varargs int id(string str, int lvl)
814{
815 if (::id(str))
816 return 1;
817 if (Query(P_INVIS) && lvl < QueryProp(P_LEVEL))
818 return 0;
819 if (QueryProp(P_GHOST)&& str == "geist von "+ lower_case(QueryProp(P_NAME)))
820 return 1;
821 if (QueryProp(P_FROG) && str == "frosch") return 1;
822 return(0);
823}
824
825/** Setzt Spielerhomepage (Spielerkommando).
826 * @param[in] str Spielereingabe
827 * @return 1 bei Erfolg, 0 sonst.
828 */
829static int set_homepage(string str)
830{
831 mixed tmp;
832 if (!(str=_unparsed_args())) {
833 if (!QueryProp(P_HOMEPAGE))
834 write("Du hast keine URL-Adresse gesetzt!\n");
835 else
836 write("Deine offizielle URL-Adresse lautet: " + QueryProp(P_HOMEPAGE)
837 +"\n");
838 return 1;
839 }
840 write("Deine offizielle URL-Adresse wurde geaendert.\n");
841 if (str=="keine")
842 SetProp(P_HOMEPAGE, 0);
843 else {
844 tmp = filter(regexplode(str, "[<][^>]*[>]"),
845 lambda(({'e}), ({#'!=, ({#'[, 'e, 0}), '<'})));
846 write("Sie lautet jetzt: "+(str = implode(tmp, ""))+"\n");
847 SetProp(P_HOMEPAGE, str);
848 }
849 return 1;
850}
851
852/** Setzt Spieler-Wohnort (Spielerkommando).
853 * \param[in] str Spielereingabe
854 * \return 1 bei Erfolg, 0 sonst.
855 */
856static int set_location( string str )
857{
858 mixed ort;
859
860 if ( str == "0" || str == "loeschen" ){
861 Set( P_LOCATION, 0 );
862 write( "Du loescht Deine Ortsangabe.\n" );
863 return 1;
864 }
865
866 if ( stringp(str = _unparsed_args()) && str != "" ){
867 Set( P_LOCATION, capitalize(str) );
868 printf( "Du aenderst Deine Ortsangabe auf \"%s\".\n",
869 Query(P_LOCATION) );
870 }
871 else if ( stringp(ort = Query(P_LOCATION)) )
872 printf( "Deine Ortsangabe lautet \"%s\".\n", Query(P_LOCATION) );
873 else{
874 Set( P_LOCATION, 0, F_VALUE );
875 write( "Du hast keine Ortsangabe gesetzt.\n" );
876 }
877
878 return 1;
879}
880
881/** Setzt ICQ-UIN des Spielers (Spielerkommando).
882 * \param[in] str Spielereingabe
883 * \return 1 bei Erfolg, 0 sonst.
884 */
885static int set_icq(string str) {
886 int num;
887
888 if (!str || str=="") {
889 if (!num=QueryProp(P_ICQ))
890 write("Du hast keine ICQ-Nummer gesetzt!\n");
891 else
892 printf("Deine ICQ-Nummer lautet: %d\n",num);
893 return 1;
894 }
895 if (sscanf(str,"%d",num)!=1 || !num) {
896 write("Deine ICQ-Nummer wurde geloescht.\n");
897 SetProp(P_ICQ, 0);
898 } else {
899 write("Deine ICQ-Nummer wurde geaendert.\n");
900 printf("Sie lautet jetzt: %d\n",num);
901 SetProp(P_ICQ, num);
902 }
903 return 1;
904}
905
906/** Setzt Instant Messanger vom Spieler (Spielerkommando).
907 * \param[in] str Spielereingabe
908 * \return 1 bei Erfolg, 0 sonst.
909 */
910static int set_messenger(string str) {
911 int num;
912 string s;
913
914 if (!str || str=="") {
915 if (!s=QueryProp(P_MESSENGER))
916 if (!num=QueryProp(P_ICQ))
917 write("Du hast keine Messenger-ID gesetzt.\n");
918 else
919 printf("Du hast keine Messenger-ID gesetzt, aber eine ICQ-Nummer: %d\n", num);
920 else
921 printf("Deine Messenger-ID lautet: %s\n", s);
922 return 1;
923 }
924 if (str=="loeschen" || str=="keine") {
925 write("Deine Messenger-ID wurde geloescht.\n");
926 SetProp(P_MESSENGER, 0);
927 } else {
928 s = _unparsed_args();
929 printf("Deine Messenger-ID lautet nun: %s\n", s);
930 SetProp(P_MESSENGER, s);
931 }
932 return 1;
933}
934
935
936// Prueft, ob der String vermutlich eine eMail-Adresse ist.
937// dies ist nicht narrensicher, wird aber die meisten eMail-Adressen zulassen
938// und viel Schrott ablehnen.
939private string check_email(string str) {
940 if (!stringp(str)) return 0;
941 return regmatch(lower_case(str),
942 "[a-z0-9._%+-]+@(?:[a-z0-9-]+\.)+[a-z]{2,4}",RE_PCRE);
943}
944
945/** Setzt Email-Adresse des Spielers (Spielerkommando).
946 * \param[in] str Spielereingabe
947 * \return 1 bei Erfolg, 0 sonst.
948 */
949static int set_email(string str)
950{
951 if (!(str=_unparsed_args())) {
952 write("Deine offizielle Email-Adresse lautet: " + QueryProp(P_MAILADDR)
953 +"\n");
954 return 1;
955 }
956 str = check_email(str);
957 if (!str) {
958 notify_fail("Deine Eingabe scheint keine gueltige EMail-Adresse "
959 "zu sein.\n");
960 return 0;
961 }
962 write("Deine EMail-Adresse wurde geaendert zu:\n"
963 +str+"\n");
964 SetProp(P_MAILADDR, str);
965 return 1;
966}
967
968/** Spielerkommando 'selbstloeschung'.
969 * Gibt Meldung aus und fragt nach einer Bestaetigung.
970 * \return 1 bei Erfolg, 0 sonst.
971 */
972static int self_delete()
973{
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:
Zesstra4eb67dc2016-12-17 19:56:31 +01002050 ReceiveMsg(capitalize(destpl->name()) + "->" + str, MT_COMM|MT_FAR,
2051 MA_EMOTE, 0, ME);
MG Mud User88f12472016-06-24 23:31:02 +02002052 break;
2053 case MSG_BUFFERED:
2054 write( capitalize(destpl->name(WER) + " ist gerade beschaeftigt.\n") );
2055 break;
2056 case MSG_IGNORED:
2057 write( capitalize(destpl->name(WER) + " ignoriert Dich.\n") );
2058 break;
2059 case MSG_VERB_IGN:
2060 case MSG_MUD_IGN:
2061 write( capitalize(destpl->name(WER) + " ignoriert Deine Meldung.\n") );
2062 break;
2063 default:
2064 write( capitalize(destpl->name(WER) + " kann Dich gerade nicht "
2065 "wahrnehmen.\n") );
2066 }
2067 return 1;
2068}
2069
2070/** Spielerkommando fuers emoten im Genitiv ';'.
2071 * Ruft emote(string,int) auf.
2072 */
2073static int gemote(string str)
2074{
2075 return emote(str, 1);
2076}
2077
2078/** Spielerkommando fuers remoten im Genitiv 'r;'.
2079 * Ruft remote(string, int) auf.
2080 */
2081static int gremote(string str)
2082{
2083 return remote(str, 1);
2084}
2085
2086static void load_auto_objects(mapping map_ldfied);
2087
2088private void InitPlayer();
2089private void InitPlayer2();
2090private void InitPlayer3();
2091
2092/** Gibt eine Zufallszahl um P_AVERAGE_SIZE herum zurueck.
2093 * \return Zufaellige Groesse.
2094 */
2095private int RandomSize()
2096{
2097 return (100+random(13)-random(13)+random(13)-random(13))*
2098 (QueryProp(P_AVERAGE_SIZE)||170)/100;
2099}
2100
2101/** Setzt bestimmte Props im Spieler, falls diese nicht gesetzt sind oder
2102 * loescht obsolete Props. Repariert bestimmte Datenstrukturen im Spieler.
2103 * Wird von start_player() nach Laden des Savefiles gerufen.
2104 * Momentan wird z.B. die Groesse gesetzt, falls sie bisher 0 ist und die
2105 * Skills des Spielers initialisiert bzw. repariert oder auf die aktuellste
2106 * Version des Skillsystems
2107 * Ruft ggf. InitSkills() und FixSkills().
2108 * @param[in] newflag Gibt an, ob es ein neuerstellter Spieler ist.
2109 * @sa start_player(), InitSkills(), FixSkills()
2110 */
2111private void updates_after_restore(int newflag) {
2112
2113 // Seher duerfen die Fluchtrichtung uebermitteln lassen.
2114 // Eigentlich koennte es Merlin machen. Dummerweise gibt es ja auch alte
2115 // Seher und dann kann es gleiche fuer alle hier gemacht werden. (Ob der
2116 // Code jemals rauskann?)
2117 //TODO: Irgendwann alle Seher korrigieren und Code nach Merlin schieben...
2118 if (IS_SEER(ME))
2119 SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS) | CAN_REPORT_WIMPY_DIR);
2120
2121 // ggf. Invis-Eigenschaft aus dem Loginobjekt abrufen (Invislogin), koennte
2122 // ja anders als aus Savefile sein. Gesetztes P_INVIS aus diesem aber
2123 // beibehalten.
2124 if (IS_LEARNER(ME) && !QueryProp(P_INVIS)
2125// && load_name(previous_object()) == "/secure/login"
2126 )
2127 {
2128 SetProp(P_INVIS, previous_object()->query_invis());
2129 if (QueryProp(P_INVIS))
2130 tell_object(ME, "DU BIST UNSICHTBAR!\n" );
2131 }
2132 "*"::updates_after_restore(newflag);
2133
2134 attributes::UpdateAttributes();
2135
2136 int size=Query(P_SIZE);
2137 if (!size) size=RandomSize();
2138 while(size==QueryProp(P_AVERAGE_SIZE))
2139 size=RandomSize();
2140 Set(P_SIZE,size);
2141
2142 // Prop wird nicht mehr genutzt. TODO: irgendwann entfernen.
2143 Set(P_SECOND_LIST, SAVE, F_MODE_AD);
2144 Set(P_SECOND_LIST, 0, F_VALUE);
2145}
2146
2147
2148/** Setzt den HC-Modus.
2149 */
2150varargs nomask void set_hc_play(string str,int val)
2151{
2152 string str1;
2153
2154 str1 = explode( object_name(previous_object()), "#" )[0];
2155
2156 if ( str1 != "/secure/login" &&
2157 previous_object()!=this_object() &&
2158 extern_call() &&
2159 (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
2160 capitalize(geteuid(ME)) != str ||
2161 geteuid(ME) != geteuid(previous_object())) ){
2162 write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
2163 return;
2164 }
2165
2166 hc_play=val;
2167}
2168
2169/** gibt den HC-Modus zurueck.
2170 */
2171nomask int query_hc_play()
2172{
2173 return hc_play;
2174}
2175
2176/** Initialisiert und aktiviert das Spielerobjekt.
2177 * Kann nur von /secure/login oder /secure/master gerufen werden.
2178 * Startet Telnet Negotiation, laedt Savefile, setzt einige Props.
2179 * Ruft updates_after_restore(), um div. Daten zu aktualisieren/reparieren.
2180 * Ruft create() aus /std/player/potion.c.
2181 * Bei neuem Spieler wird der entsprechende Event ausgeloest die Attribute
2182 * auf die Startwerte gesetzt.
2183 * Prueft Zweitiemarkierungen.
2184 * Ruft InitPlayer().
2185 * @param[in] str Name des Charakters, der geladen werden soll.
2186 * @param[in] ip textuelle Repraesentation der IP-Adresse des Spielers.
2187 * @return 1 bei Erfolg, 0 sonst.
2188 * @todo Div. Reparaturen/Updates nach updates_after_restore() auslagern.
2189 */
2190varargs nomask int start_player( string str, string ip )
2191{
2192 mixed second;
2193 int newflag; /* could player be restored? */
2194 string str1;
2195
2196 call_out( "disconnect", 600 );
2197
2198 str1 = explode( object_name(previous_object()), "#" )[0];
2199
2200 if ( str1 != "/secure/login" &&
2201 str1 != "/secure/master" &&
2202 (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
2203 capitalize(geteuid(ME)) != str ||
2204 geteuid(ME) != geteuid(previous_object())) ){
2205 write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
2206 destruct(ME);
2207 return 0;
2208 }
2209
2210 /* try to restore player. If it doesn't exist, set the new flag */
2211 newflag = !restore_object( "/" + SAVEPATH + lower_case(str)[0..0] + "/"
2212 +lower_case(str) );
2213
2214 updates_after_restore(newflag);
2215
2216 if ( query_once_interactive(ME) )
2217 {
2218 // Telnet-Negotiations durchfuehren, aber nur die grundlegenden aus
2219 // telnetneg. Alle anderen sollten erst spaeter, nach vollstaendiger
2220 // Initialisierung gemacht werden.
2221 telnetneg::startup_telnet_negs();
2222 modify_prompt();
2223 Set( P_LAST_LOGIN, time() );
2224 }
2225
2226 Set( P_WANTS_TO_LEARN, 1 ); // 1 sollte der default sein !!!
2227 Set( P_WANTS_TO_LEARN, PROTECTED, F_MODE_AS );
2228 // Eingefuegt 18.11.99, kann nach einem Jahr wieder raus:
2229 Set( P_TESTPLAYER, PROTECTED, F_MODE_AS );
2230
2231 if ( IS_LEARNER(ME) )
2232 SetProp( P_CAN_FLAGS, QueryProp(P_CAN_FLAGS)|CAN_REMOTE );
2233
2234 Set( P_NAME, str );
2235 Set( P_NAME, SECURED, F_MODE_AS );
2236
2237 if ( !QueryProp(P_NEEDED_QP) )
2238 SetProp( P_NEEDED_QP, REQ_QP );
2239
2240 Set( P_NEEDED_QP, NOSETMETHOD, F_SET_METHOD );
2241 Set( P_NEEDED_QP, SAVE|SECURED, F_MODE_AS );
2242
2243 /* autosave the player after 500 heartbeats */
2244 time_to_save = age + 500;
2245 potion::create(); /* DO IT HERE AFTER THE RESTORE !! */
2246
2247 AddId( getuid() );
2248 SetProp( P_AC, 0 );
2249 SetProp( P_WEAPON, 0 );
2250
2251 /* Set some things which wont be set when all is OK */
2252 SetProp( P_MAX_HP, (QueryAttribute(A_CON) * 8 + 42 ));
2253 SetProp( P_MAX_SP, (QueryAttribute(A_INT) * 8 + 42 ));
2254
2255 catch( bb = "/secure/bbmaster"->query_bb() );
2256
2257 /* If this is a new character, we call the adventurers guild to get
2258 * our first title !
2259 */
2260 if ( newflag ) {
2261 if ( QueryGuest())
2262 SetProp( P_TITLE, "ueberkommt das "MUDNAME" ..." );
2263
2264 Set( P_LEVEL, -1 );
2265 SetProp( P_ATTRIBUTES, ([ A_STR:1, A_CON:1, A_INT:1, A_DEX:1 ]) );
2266 SetProp( P_HP, QueryProp(P_MAX_HP) );
2267
2268 // Event ausloesen
2269 EVENTD->TriggerEvent(EVT_LIB_PLAYER_CREATION, ([
2270 E_OBJECT: ME,
2271 E_PLNAME: getuid(ME) ]) );
2272 }
2273
2274 InitPlayer();
2275
2276 // Padreic 01.02.1999
2277 if ( !IS_LEARNER(ME) && second = QueryProp(P_SECOND) ) {
2278 if ( stringp(second) && lower_case(second)[0..3] == "von " ) {
2279 second = lower_case(second[4..]);
2280 SetProp( P_SECOND, second );
2281 }
2282
2283 if ( !stringp(second ) ||
2284 file_size( "/save/" + second[0..0] + "/" + second + ".o" ) <= 0 ){
2285 if ( stringp(second) &&
2286 file_size( "/save/" + lower_case(second[0..0]) + "/" +
2287 lower_case(second) + ".o" ) >0 ){
2288 SetProp( P_SECOND, lower_case(second) );
2289 log_file( "WRONG_SECOND",
2290 sprintf( "%s: %s: P_SECOND = %O -> Automatisch "
2291 "korrigiert,\n",
2292 dtime(time()), object_name(), second ) );
2293 }
2294 else {
2295 tell_object( ME,
2296 "*\n*\n* Deine Zweitiemarkierung ist ungueltig, "
2297 "bitte aendere diese und sprich im\n* Zweifel "
2298 "bitte einen Erzmagier an.\n*\n*\n" );
2299
2300 log_file( "WRONG_SECOND",
2301 sprintf( "%s: %s: P_SECOND = %O\n",
2302 dtime(time()), object_name(), second ) );
2303 // ein bisschen deutlicher auffordern.. Padreic 08.04.1999
2304 move( "/d/gebirge/room/zwafflad", M_GO );
2305 }
2306 }
2307 }
2308 return(0);
2309}
2310
2311/** Letzte Phase der Spielerinitialisierung beim Laden des Charakters.
2312 * Ruft enable_commands(), aktiviert den Heartbeat und aktiviert die
2313 * Kommandos aus den geerbten command.c, put_and_get.c, team.c, soul.c,
2314 * guide.c, setzt den Living Name.
2315 * Registriert UseSpell() als Catchall-Kommando.
2316 * Laesst den Spieler ggf. einen Level per /std/gilde aufsteigen, ruft
2317 * call_notify_player_change(), loest Login-Event aus.
2318 * Gibt Willkommenstexte, News und neue Mails aus.
2319 * Findet den Startraum und bewegt den Spieler dorthin.
2320 * Ruft FinalSetup() (aus den Rassenshells).
2321 * Begrenzt Geldmenge im Spieler (wegen Bankzweities) nach Tragkraft und
2322 * erstattet Geld bei Reboot.
2323 * Ruft set_is_wizard().
2324 * Startet Clonen der Autoloader.
2325 * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
2326 */
2327private void InitPlayer4()
2328{
2329 int num, str, ski;
2330 string err, called_from_ip;
2331 mixed start_place;
2332 object mon;
2333
2334 enable_commands();
2335 set_heart_beat(1);
2336 command::initialize();
2337 add_put_and_get_commands();
2338 add_team_commands();
2339 add_soul_commands();
2340 add_guide_commands();
2341 add_action( "UseSpell", "", 1 );
2342 set_living_name( getuid() );
2343 while ( remove_call_out("disconnect") != -1 )
2344 ;
2345
2346 if ( QueryProp(P_LEVEL) == -1 )
2347 {
2348 catch( "/std/gilde"->try_player_advance(this_object()) ;publish );
2349 }
2350
2351 if ( interactive(ME) )
2352 call_notify_player_change(1);
2353
2354 if ( interactive(this_object()) ) {
2355 cat( "/etc/NEWS" );
2356
2357 NewbieIntroMsg();
2358
2359 if ( QueryProp(P_INVIS) && !IS_WIZARD(ME) )
2360 SetProp( P_INVIS, 0 );
2361
2362 catch( num = "secure/mailer"->FingerMail(getuid());publish );
2363
2364 if ( num )
2365 write( "Du hast " + num + " neue" +
2366 (num == 1 ? "n Brief" : " Briefe")+" im Postamt liegen.\n" );
2367
2368 catch( RegisterChannels();publish );
2369
2370 if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
2371 query_ip_number(ME) != called_from_ip ){
2372 string tmp;
2373
2374 if ( stringp(tmp = query_ip_name(called_from_ip)) &&
2375 tmp != called_from_ip )
2376 tmp = " [" + tmp + "]";
2377 else
2378 tmp = "";
2379
2380 write( "Das letzte Mal kamst Du von " + called_from_ip
2381 + tmp + ".\n" );
2382 }
2383
2384 Set( P_CALLED_FROM_IP, query_ip_number(ME) );
2385 }
2386
2387 if ( !stringp(default_home) || default_home == "" )
2388 default_home = "/gilden/abenteurer";
2389
2390 if ( IS_SEER(ME) && !IS_LEARNER(ME) )
2391 catch( start_place = HAUSVERWALTER->FindeHaus(getuid(ME));publish );
2392 // wenn der Spieler noch ganz frisch ist und noch wenig Stufenpunkte
2393 // gekriegt hat und das Tutorial noch nicht gemacht hat, startet er im
2394 // Tutorial.
2395 else if (QueryProp(P_LEP) <= 120
2396 && QM->HasMiniQuest(this_object(),
2397 "/d/anfaenger/ennox/tutorial/npcs/verkaeufer") != 1)
2398 start_place = "/room/welcome/"+ getuid(this_object());
2399 else
2400 start_place = 0;
2401
2402 if ( !start_place )
2403 start_place = QueryProp(P_START_HOME);
2404
2405 if( objectp(start_place) || (stringp(start_place) && start_place != "" ) ){
2406 if ((err = catch(move( start_place, M_GO|M_SILENT|M_NO_SHOW );publish))
2407 || !environment() )
2408 err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
2409 }
2410 else
2411 err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
2412
2413 if ( err )
2414 catch(move( "/gilden/abenteurer", M_GO|M_SILENT|M_NO_SHOW );publish);
2415
2416 // Die Shell muss FinalSetup() nicht implementieren. Daher Callother
2417 catch( ME->FinalSetup();publish );
2418
2419 // Login-event ausloesen
2420 EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
2421 E_OBJECT: ME,
2422 E_PLNAME: getuid(ME),
2423 E_ENVIRONMENT: environment() ]) );
2424
2425 // erst jetzt GMCP freigeben und zu verhandeln.
2426 gmcp::startup_telnet_negs();
2427
2428 // Schonmal sichern, falls ein Bug (Evalcost...) dazwischen kommen sollte.
2429 autoload_rest = autoload;
2430
2431 // Um Geld-Xties mit illegal viel Geld zu vermeiden, kommt ein Check:
2432 if ( !IS_LEARNER(ME) && !Query(P_TESTPLAYER) &&
2433 mon = present( "\ngeld", ME ) ){
2434 // maximale Kraft, die der Spieler haette haben koennen
2435 str = QueryAttribute(A_STR) + 4;
2436 if ( Query(P_FROG) )
2437 str += 30;
2438 if ( str < 1 )
2439 str = QueryRealAttribute(A_STR) + 4;
2440 if ( str > 30 )
2441 str = 30;
2442
2443 // Trageskill beachten
2444 ski = to_int(UseSkill( SK_CARRY, ([SI_SKILLARG : str ]) ));
2445 if ( !intp(ski) )
2446 ski = 0;
2447
2448 // Wieviel konnte der Spieler insgesamt maximal tragen?
2449 num = 9200 + str * 800 + ski;
2450 if ( num < 3000 )
2451 num = 3000;
2452
2453 // Verdoppeln fuer einen guten Container und nochmal 20% draufschlagen
2454 // zur Sicherheit. Das Ganze danach *4, um die maximale Anzahl Muenzen
2455 // zu erhalten.
2456 num = (int) (num * 8.8);
2457
2458 // Geld, das zuviel ist, auf den Maximalwert kuerzen.
2459 // Zur Sicherheit wird mitgeloggt. Falls ein Spieler sich (zu recht)
2460 // beschwert, kann man immer noch wieder die Summe korrigieren ;-)
2461 if ( (str = mon->QueryProp(P_AMOUNT)) > num ){
2462 mon->SetProp( P_AMOUNT, num );
2463 log_file( "ZUVIEL_GELD", sprintf( "%s: %s hatte %d Muenzen bei "
2464 "sich. Korrigiert auf %d.\n ",
2465 ctime(time())[4..],
2466 capitalize(getuid(ME)),
2467 str, num ) );
2468 }
2469 }
2470 int entschaedigung = QueryProp(P_CARRIED_VALUE);
2471 if ( entschaedigung > 0 )
2472 {
2473 write( "Du findest " + entschaedigung +
2474 " Muenzen, die Du beim letzten Mal verloren hast.\n" );
2475
2476 if ( MayAddWeight( entschaedigung / 4 ) ){
2477 write( "Weil Du nicht mehr soviel tragen kannst, spendest Du den "+
2478 "Rest der Zentralbank.\n" );
2479
2480 num = (QueryProp(P_MAX_WEIGHT) - query_weight_contents()) * 4;
2481 entschaedigung = (num < 0) ? 0 : num;
2482 }
2483
2484 AddMoney( entschaedigung );
2485 SetProp(P_CARRIED_VALUE,0);
2486 }
2487
2488 if ( !QueryProp(P_INVIS) )
2489 say( capitalize(name(WER)) + " betritt diese Welt.\n" );
2490 else
2491 write( "DU BIST UNSICHTBAR!\n\n" );
2492#if __EFUN_DEFINED__(set_is_wizard)
2493 if ( IS_WIZARD(getuid(ME)) )
2494 set_is_wizard( ME, 1 );
2495 else
2496 set_is_wizard( ME, 0 );
2497#endif
2498 if ( query_once_interactive(ME) )
2499 ListAwaited();
2500
2501 // Autoloader werden ganz zum Schluss geclont, da das bis zum bitteren
2502 // (Evalcost-)Ende geschieht und danach u.U. keine Rechenzeit fuer
2503 // andere Aktionen mehr ueber ist
2504 load_auto_objects( autoload );
2505}
2506
2507/** Setzt die Spielerinitialisierung nach start_player() fort.
2508 * Prueft den Wert der vom Spieler getragenen Sachen (fuer Entschaedigung
2509 * nach Reboot).
2510 * Setzt div. Properties des "Spielerkoerpers", die eigentlich gespeichert
2511 * werden, nach einem Reboot zurueck.
2512 * Fragt ggf. nach eMail-Adresse und uebergibt per input_to an
2513 * InitPlayer2().
2514 * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
2515*/
2516private void InitPlayer()
2517{
2518 string mailaddr;
2519
2520 // wenn es einen Crash gab, sollen Spieler nicht noch extra bestraft werden
2521 if ( file_time( "/save/" + getuid()[0..0] + "/" + getuid() + ".o" )
2522 < last_reboot_time() ){
2523 SetProp( P_FOOD, 0 );
2524 SetProp( P_DRINK, 0 );
2525 SetProp( P_ALCOHOL, 0 );
2526 SetProp( P_BLIND, 0 );
2527 SetProp( P_DEAF, 0 );
2528 SetProp( P_POISON, 0 );
2529 SetProp( P_GHOST, 0 );
2530 SetProp( P_FROG, 0 );
2531 SetProp( P_HP, QueryProp(P_MAX_HP) );
2532 SetProp( P_SP, QueryProp(P_MAX_SP) );
2533 }
2534
2535 if ( QueryGuest() )
2536 Set( P_MAILADDR, "none" );
2537 else if ( !(mailaddr = Query(P_MAILADDR)) || mailaddr == "" ) {
2538 write(break_string(
2539 "Eine gueltige EMail-Adresse erleichtert es erheblich, Dir "
2540 "ein neues Passwort setzen zu lassen, falls Du einmal Dein "
2541 "Passwort vergisst.",78));
2542 input_to( "getmailaddr",INPUT_PROMPT,
2543 "Gib bitte Deine EMail-Adresse an: " );
2544 return;
2545 }
2546 InitPlayer2();
2547}
2548
2549/** liest eMail-Adresse vom Spieler ein und speichert sie.
2550 * Uebergibt anschliessend an InitPlayer2()
2551 * @param[in] maddr Spielereingabe der Emailadresse.
2552 * @sa InitPlayer2().
2553 */
2554static void getmailaddr( string maddr )
2555{
2556 maddr = check_email(maddr);
2557
2558 if ( !stringp(maddr)) {
2559 write("Deine Eingabe scheint keine gueltige EMail-Adresse gewesen "
2560 "zu sein.\n");
2561 input_to( "getmailaddr", INPUT_PROMPT,
2562 "Gib bitte Deine EMail-Adresse an: " );
2563 return;
2564 }
2565 Set( P_MAILADDR, maddr );
2566 InitPlayer2();
2567}
2568
2569
2570/** Prueft Geschlecht des Spielers und fragt ggf. beim Spieler nach.
2571 * Uebergibt an InitPlayer3() oder get_gender().
2572 * @sa InitPlayer3(), get_gender().
2573 */
2574private void InitPlayer2()
2575{
2576 if( member(({ MALE, FEMALE }), QueryProp(P_GENDER) ) == -1 ) {
2577 input_to( "getgender", INPUT_PROMPT,
2578 "Bist Du maennlich oder weiblich: ");
2579 return;
2580 }
2581
2582 InitPlayer3();
2583}
2584
2585/** Liest Spielerantwort auf die Frage nach dem Geschlecht des Chars ein.
2586 * Wird gerufen von input_to().
2587 * Uebergibt an InitPlayer3().
2588 * @sa InitPlayer3().
2589 */
2590static void getgender( string gender_string )
2591{
2592 gender_string = lower_case( gender_string );
2593
2594 if ( sizeof(gender_string)==1 && gender_string[0] == 'm' ){
2595 write( "Willkommen, mein Herr!\n" );
2596 SetProp( P_GENDER, MALE );
2597 }
2598 else if ( sizeof(gender_string)==1 && gender_string[0] == 'w' ){
2599 write( "Willkommen, gnae' Frau!\n" );
2600 SetProp( P_GENDER, FEMALE );
2601 }
2602 else {
2603 write( "Wie? Was? Verstehe ich nicht!\n" );
2604 input_to( "getgender", INPUT_PROMPT,
2605 "Bist Du maennlich oder weiblich? (tippe m oder w): ");
2606 return;
2607 }
2608
2609 InitPlayer3();
2610}
2611
2612
2613/** Prueft Terminaltyp des Spielers und fragt ggf. beim Spieler nach.
2614 * Uebergibt an InitPlayer4() oder gettty().
2615 * @sa InitPlayer4(), gettty().
2616 */
2617private void InitPlayer3()
2618{
2619 if ( !QueryProp(P_TTY) || QueryProp(P_TTY) == "none" )
2620 {
2621 write( "Waehle einen Terminaltyp (kann spaeter mit <stty> geaendert "
2622 "werden)\n");
2623 input_to( "gettty", INPUT_PROMPT, "vt100, ansi, dumb (Standard: dumb): " );
2624 return;
2625 }
2626 InitPlayer4();
2627}
2628
2629/** Liest Spielerantwort auf die Frage nach dem Terminaltyp ein.
2630 * Wird gerufen von input_to().
2631 * Uebergibt an InitPlayer4().
2632 * @sa InitPlayer4().
2633 */
2634static void gettty( string ttystr )
2635{
2636 if ( !ttystr || ttystr == "" )
2637 ttystr = "dumb";
2638
2639 ttystr = lower_case(ttystr);
2640
2641 if ( ttystr == "vt100" ){
2642 write( "Dies sollte " + ANSI_BOLD + "fett" + ANSI_NORMAL + " sein.\n" );
2643 SetProp( P_TTY, ttystr );
2644 }
2645 else
2646 if ( ttystr == "ansi" ){
2647 write( "Dies sollte " + ANSI_RED + "rot" + ANSI_NORMAL +
2648 " sein.\n" );
2649 SetProp( P_TTY, "ansi" );
2650 }
2651 else if ( ttystr == "dumb" ){
2652 write( "Ohje, oede! Besorg Dir ein besseres Terminal!\n" );
2653 SetProp( P_TTY, "dumb" );
2654 }
2655 else {
2656 write( "Dieser Terminaltyp wird nicht unterstuetzt. Nimm bitte "
2657 "einen aus:\nvt100, ansi or dumb (Standard ist dumb).\n" );
2658 input_to( "gettty", INPUT_PROMPT,
2659 "vt100, ansi or dumb (Standard ist dumb): ");
2660 return;
2661 }
2662
2663 InitPlayer4();
2664}
2665
2666
2667/** Liefert die UID des Charakters zurueck, also den Charakternamen.
2668 * @return UID des Objekts (Charaktername).
2669 */
2670nomask string query_real_name() {
2671 /* ACHTUNG !! DIES LFUN DARF NICHT ENTFERNT WERDEN !!! */
2672 /* Sie wird vom Gamedriver (zB bei F_ED) aufgerufen !! */
2673 /* Ich bin da zwar nicht so ueberzeugt von, dass der Driver die heutzutage
2674 * noch ruft, aber die halbe Mudlib ruft sie. ;-) (Zesstra, 27.4.08)
2675 */
2676 return getuid();
2677}
2678
2679/*
2680 * the wizard command review: show player moving messages
2681 */
2682int review() {
2683 string *msg;
2684 write(short());
2685 write("Deine Bewegungen werden wie folgt gemeldet:\n"+
2686 "mout: "+name(WER)+" "+(msg=explode(QueryProp(P_MSGOUT),"#"))[0]
2687 +" <Richtung>"+(sizeof(msg)>1 ? msg[1] : "")+".\n"+
2688 "min: "+name(WER)+" "+QueryProp(P_MSGIN)+".\n"+
2689 "mmout: "+name(WER)+" "+QueryProp(P_MMSGOUT)+".\n"+
2690 "mmin: "+name(WER)+" "+QueryProp(P_MMSGIN)+".\n"+
2691 (IS_LEARNER(ME) ?
2692 "cmsg: "+name(WER)+" "+QueryProp(P_CLONE_MSG)+".\n"+
2693 "dmsg: <Irgendetwas> "+QueryProp(P_DESTRUCT_MSG)+".\n"
2694 : "")+
2695 "Wenn Du jemanden angreifst, sieht das so aus:\n"+
2696 name(WER)+" greift Dich"+QueryProp(P_HANDS)[0]+" an.\n");
2697 return 1;
2698}
2699
2700/*
2701 * set player moving messages
2702 */
2703
2704static int setmin(string str)
2705{
2706 SetProp(P_MSGIN, _unparsed_args()||"kommt an");
2707 write("Ok.\n");
2708 return 1;
2709}
2710
2711static int setmout(string str)
2712{
2713 string *msg;
2714
2715 if(sizeof(msg=explode((_unparsed_args()||"geht"),"#"))>2)
2716 {
2717 write("Du darfst nur einmal '#' fuer die Richtung angeben.\n");
2718 return 1;
2719 }
2720 if(sizeof(msg)>1)
2721 {
2722 if (msg[0]!="" && msg[0][<1]==' ') msg[0]=msg[0][0..<2];
2723 SetProp(P_MSGOUT, msg[0]+"#"+msg[1]);
2724 }
2725 else
2726 SetProp(P_MSGOUT, _unparsed_args()||"geht");
2727 write("Ok.\n");
2728 return 1;
2729}
2730
2731static int setmmin(string str)
2732{
2733 SetProp(P_MMSGIN, _unparsed_args()||"erscheint");
2734 write("Ok.\n");
2735 return 1;
2736}
2737
2738static int setmmout(string str)
2739{
2740 SetProp(P_MMSGOUT, _unparsed_args()||"verschwindet");
2741 write("Ok.\n");
2742 return 1;
2743}
2744
2745static int setcmsg(string str)
2746{
2747 SetProp(P_CLONE_MSG, _unparsed_args()||"zaubert etwas aus "
2748 + QueryPossPronoun(MALE,WEM) + " Aermel hervor");
2749 write("Ok.\n");
2750 return 1;
2751}
2752
2753static int setdmsg(string str)
2754{
2755 SetProp(P_DESTRUCT_MSG, _unparsed_args()||"wird von " + name(WER,1)
2756 + " zerstaeubt");
2757 write("Ok.\n");
2758 return 1;
2759}
2760
2761static int set_title(string str)
2762{
2763 string bonus;
2764
2765 if(!IS_SEER(this_object()) && !FAO_HAS_TITLE_GIFT(this_object()) )
2766 {
2767 return 0;
2768 }
2769
2770 bonus = "";
2771 if (!(str=_unparsed_args()))
2772 str = "";
2773 else if( str[0..2]=="\\b," || str[0..2]=="\\b'" )
2774 {
2775 bonus = "\b"; // ein backspace fuer ein (hoch)komma ist ok! :-)
2776 str = str[2..];
2777 }
2778 if(str=="0") // damit der Gildentitel zum Vorschein kommen kann
2779 SetProp(P_TITLE, 0);
2780 else
2781 SetProp(P_TITLE, bonus+str);
2782 write("Ok.\n");
2783 return 1;
2784}
2785
2786static int extra_input(string str, string look)
2787{
2788 if (str=="**")
2789 {
2790 if (look=="")
2791 SetProp(P_EXTRA_LOOK,0);
2792 else
2793 SetProp(P_EXTRA_LOOK,look);
2794 write("Ok.\n");
2795 return 1;
2796 }
2797 input_to("extra_input",INPUT_PROMPT, "]" ,look+str+"\n");
2798 return 1;
2799}
2800
2801static int extralook(mixed str)
2802{
2803 if( str=="?" )
2804 {
2805 write( "Dein Extralook ist : "+QueryProp(P_EXTRA_LOOK) + "\n");
2806 return 1;
2807 }
2808 write("Bitte gib Deinen Extra-Look ein. Beenden mit **:\n");
2809 input_to("extra_input",INPUT_PROMPT,"]","");
2810 return 1;
2811}
2812
2813static void calculate_value()
2814{
2815 int i, carried_value, value;
2816
2817 carried_value=0;
2818 foreach(object ob: deep_inventory(ME)) {
2819 if (!ob->QueryProp(P_AUTOLOADOBJ))
2820 carried_value+=((value=(int)ob->QueryProp(P_VALUE)) > 1000 ? 1000 : value);
2821 }
2822 SetProp(P_CARRIED_VALUE, carried_value);
2823}
2824
2825/* Called by command 'save' */
2826int save_character() {
2827 save_me(1);
2828 write("Ok.\n");
2829 return 1;
2830}
2831
2832void save_me(mixed value_items)
2833{
2834 // Gaeste werden nicht gespeichert
2835 if( getuid()[0..3]=="gast" )
2836 return;
2837
2838 // Autoloader identifizieren und speichern
2839 autoload=([]);
2840 foreach(object ob: deep_inventory(ME)) {
2841 int val = ob->QueryProp( P_AUTOLOADOBJ );
2842 if (val && clonep(ob))
2843 {
2844 string obname=load_name(ob);
2845 if (obname == GELD)
2846 autoload[obname] += val;
2847 else
2848 autoload += ([obname:val]);
2849 }
2850 }
2851 // An noch nicht geclonte Autoloader denken!
2852 autoload += autoload_rest;
2853
2854 // Im Bedarfsfall Wert des Inventory bestimmen
2855 if (value_items)
2856 calculate_value();
2857 else
2858 SetProp(P_CARRIED_VALUE, 0);
2859
2860 // Logout-Zeit speichern
2861 if(query_once_interactive(ME) && !QueryProp(P_INVIS))
2862 Set(P_LAST_LOGOUT,time());
2863
2864 // Funktion zur Bestimmung des Gildenrating aufrufen
2865 string gilde=GUILD_DIR+QueryProp(P_GUILD);
2866 if (find_object(gilde) || file_size(gilde+".c")>-1)
2867 catch(call_other(gilde,"GuildRating",this_object());publish);
2868
2869 // Speichern des Spielers
2870 save_object("/"+SAVEPATH+getuid()[0..0]+"/" + getuid());
2871}
2872
2873static varargs void log_autoload( string file, string reason, mixed data, string error )
2874{
2875 if (member(autoload_error,file)!=-1) return;
2876 log_file(SHELLLOG("NO_AUTO_FILE"),sprintf("%s: %s: %s\nreason: cannot %s file\ndata: %O\n%s\n",
2877 ctime()[4..15],capitalize(getuid()),file,reason,data,
2878 (error?"Fehlermeldung: "+error+"\n":"")));
2879 autoload_error+=({file});
2880}
2881
2882// tics, die fuer autoloader reichen sollten:
2883#define SAFE_FOR_AUTOLOADER __MAX_EVAL_COST__/4
2884
2885private void load_auto_object( string file, mixed data )
2886{
2887 object ob;
2888 string error;
2889
2890 if( get_eval_cost() < SAFE_FOR_AUTOLOADER ) return;
2891 m_delete( autoload_rest, file );
2892 autoload_error-=({file});
2893
2894 if ( file == "/obj/money" )
2895 file = "/items/money";
2896 if ( file == "/obj/seercard" )
2897 file = "/items/seercard";
2898
2899 ob = find_object(file);
2900
2901 if (!ob)
2902 {
2903 if (file_size(file+".c")<0&&
2904 file_size(implode(explode(file,"/")[0..<2],"/")+
2905 "/virtual_compiler.c")<0)
2906 {
2907 log_autoload(file,"find",data,0);
2908 return;
2909 }
2910 if (error = catch(load_object(file); publish))
2911 {
2912 log_autoload(file,"load",data,error);
2913 return;
2914 }
2915 }
2916 if ( error = catch(ob = clone_object(file); publish) )
2917 {
2918 log_autoload(file,"clone",data, error);
2919 return;
2920 }
2921
2922 if ( error = catch(ob->SetProp( P_AUTOLOADOBJ, data ); publish) )
2923 {
2924 log_autoload(file,"SetProp",data, error);
2925 ob->remove(1);
2926 if (ob) destruct(ob);
2927 return;
2928 }
2929
2930 if ( error = catch(ob->move( ME, M_NOCHECK );publish) ) {
2931 log_autoload(file,"move",data, error);
2932 ob->remove(1);
2933 if(ob) destruct(ob);
2934 return;
2935 }
2936}
2937
2938static void load_auto_objects( mapping map_ldfied )
2939{
2940 if ( (!mappingp(map_ldfied) || !sizeof(map_ldfied)) && !sizeof(autoload_rest) )
2941 return;
2942
2943 // Mit Netztoten Spielern rechnen manche Autoloader nicht. Also
2944 // das Clonen unterbrechen und in Reconnect() wieder anwerfen.
2945 if ( !interactive() )
2946 return;
2947
2948 // Kleiner Hack: autoload_rest ist eine globale Variable, die beim
2949 // Clonen der einzelnen Autoloader direkt veraendert wird.
2950 // So lange das Mapping noch Eintraege hat, muessen wir noch fehlende
2951 // Autoloader clonen.
2952 if ( !sizeof(autoload_rest) )
2953 autoload_rest = map_ldfied;
2954
2955 // Schon hier einen call_out() zum "Nach"clonen von noch nicht geclonten
2956 // Autoloadern starten, da spaeter u.U. keine Rechenzeit mehr dafuer da ist.
2957 while ( remove_call_out("load_auto_objects") != -1 )
2958 /* do nothing */;
2959
2960 // Mit Parameter '0' aufrufen, da das globale Mapping benutzt wird.
2961 // Verzoegerung 0 in rekursiven Callouts ist bloed, also 1s Delay
2962 call_out( "load_auto_objects", 2, 0 );
2963
2964 // Mit catch() gegen die Evalcost-Falle!
2965 // Mit Absicht das walk_mapping() aus der "alten" Version erhalten und
2966 // nicht durch eine (einfachere) Schleife inkl. get_eval_cost() ersetzt,
2967 // da eine Schleife gegenueber der walk_mapping()-Loesung den Aufbau
2968 // der previous_object()-Kette veraendern wuerde; darauf testen aber
2969 // manche Objekte.
2970 catch( walk_mapping( autoload_rest, #'load_auto_object/*'*/ ) );
2971}
2972
2973/*
2974 * al_to_title: Make the numeric alignment value into a string
2975 */
2976static string al_to_title(int a)
2977{
2978 if (a >= KILL_NEUTRAL_ALIGNMENT * 100)
2979 return "heilig";
2980 if (a > KILL_NEUTRAL_ALIGNMENT * 20)
2981 return "gut";
2982 if (a > KILL_NEUTRAL_ALIGNMENT * 4)
2983 return "nett";
2984 if (a > - KILL_NEUTRAL_ALIGNMENT * 4)
2985 return "neutral";
2986 if (a > - KILL_NEUTRAL_ALIGNMENT * 20)
2987 return "frech";
2988 if (a > - KILL_NEUTRAL_ALIGNMENT * 100)
2989 return "boese";
2990 return "satanisch";
2991}
2992
2993static int toggle_whimpy_dir(string str) {
2994 SetProp(P_WIMPY_DIRECTION,str=_unparsed_args()||str);
2995 if (str)
2996 printf("Ok, Fluchtrichtung %O.\n",str);
2997 else
2998 printf("Ok, bevorzugte Fluchtrichtung deaktiviert.\n");
2999 return 1;
3000}
3001
3002static int toggle_whimpy(string str)
3003{
3004 int i;
3005
3006 if(!str || str=="" || (sscanf(str,"%d",i)<0))
3007 {
3008 write("vorsicht <hp>, 0<=hp<"+QueryProp(P_MAX_HP)+"\n");
3009 return 1;
3010 }
3011 if(i>=QueryProp(P_MAX_HP) || i<0)
3012 {
3013 write("Der Wert ist nicht erlaubt.\n");
3014 return 1;
3015 }
3016 if(!i) write("Prinz Eisenherz-Modus.\n");
3017 else write("Vorsicht-Modus ("+i+")\n");
3018 SetProp(P_WIMPY,i);
3019 return 1;
3020}
3021
3022/** Bestimmt, ob das Spielerobjekt beschattet werden darf.
3023 * Beschatten ist nur fuer Objekte erlaubt, die in /std/player/shadows
3024 * abgelegt sind.
3025 * Ausnahme: Testspieler mit gesetztem P_ALLOWED_SHADOW
3026 * @param[in] obj Objekt, was beschatten moechte.
3027 * @return 0, wenn Beschatten erlaubt, 1 sonst.
3028 */
3029varargs nomask int query_prevent_shadow(object obj)
3030{
3031 string what, allowed_shadow;
3032 int dummy;
3033
3034// if ( Query(P_TESTPLAYER) )
3035// return 0;
3036
3037 if (obj){
3038 what=object_name(obj);
3039 if (what && what != "" &&
3040 sscanf(what,"/std/player/shadows/%s#%d",what,dummy)==2)
3041 return 0;
3042
3043 // Einem Testspieler kann man P_ALLOWED_SHADOW auf einen zu testenden
3044 // Shadow setzen.
3045 if ( Query(P_TESTPLAYER) &&
3046 stringp(what) &&
3047 stringp(allowed_shadow=Query(P_ALLOWED_SHADOW)) &&
3048 strstr(what, allowed_shadow)==0)
3049 return 0;
3050 }
3051 return 1;
3052}
3053
3054static int uhrzeit()
3055{
3056 write(dtime(time()+QueryProp(P_TIMEZONE)*3600)+".\n");
3057 return 1;
3058}
3059
3060protected string SetDefaultHome(string str)
3061{
3062 return default_home=str;
3063}
3064
3065string QueryDefaultHome()
3066{
3067 return default_home;
3068}
3069
3070protected string SetDefaultPrayRoom(string str)
3071{
3072 if(hc_play>1)
3073 {
3074 default_pray_room="/room/nirvana";
3075 }
3076 else
3077 default_pray_room=str;
3078
3079 return default_pray_room;
3080}
3081
3082string QueryPrayRoom()
3083{
3084 if(hc_play>1)
3085 {
3086 return "/room/nirvana";
3087 }
3088 string room = QueryProp(P_PRAY_ROOM);
3089 if (stringp(room))
3090 return room;
3091 else if (default_pray_room)
3092 return default_pray_room;
3093 // hoffentlich ist das wenigstens gesetzt.
3094 return default_home;
3095}
3096
3097void _restart_beat()
3098{
3099 tell_object(ME,
3100 "Der GameDriver teilt Dir mit: Dein Herzschlag hat wieder eingesetzt.\n");
3101 set_heart_beat(1);
3102}
3103
3104static int weg(string str)
3105{
3106 if (!(str=_unparsed_args()))
3107 {
3108 printf("Du bist nicht%s als abwesend gekennzeichnet.\n",
3109 QueryProp(P_AWAY) ? " mehr" : "");
3110 SetProp(P_AWAY, 0);
3111 return 1;
3112 }
3113 write("Du bist jetzt als abwesend gekennzeichnet.\n");
3114 SetProp(P_AWAY, str);
3115 return 1;
3116}
3117
3118/* Ein Befehl zum anschauen der Wegmeldung anderer Spieler */
3119static int wegmeldung(string player)
3120{
3121
3122 object player_ob;
3123 string weg;
3124
3125 if ( !player || player=="" ||
3126 player==lowerstring(this_player()->QueryProp(P_NAME)))
3127 {
3128 weg=this_player()->QueryProp(P_AWAY);
3129 write ("Du bist "+(weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
3130 if (weg)
3131 write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
3132 return 1;
3133 }
3134
3135 // Welcher Spieler ist gemeint?
3136 player_ob=find_player(player);
3137
3138 // Spieler nicht da oder Invis und Anfrager is kein Magier
3139 if (!player_ob ||
3140 (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
3141 {
3142 write(capitalize(player)+" ist gerade nicht im Spiel.\n");
3143 return 1;
3144 }
3145
3146 weg=player_ob->QueryProp(P_AWAY);
3147
3148 // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
3149 write (player_ob->QueryProp(P_NAME)+" ist "+
3150 (weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
3151
3152 if (weg)
3153 write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
3154
3155 return 1;
3156}
3157
3158static string timediff(int time)
3159{
3160 string ret;
3161
3162 ret="";
3163 if(time>=86400) {
3164 ret+=time/86400+"d ";
3165 time%=86400;
3166 }
3167 if(time<36000) ret+="0";
3168 ret+=time/3600+":";
3169 time%=3600;
3170 if(time<600) ret+="0";
3171 ret+=time/60+":";
3172 time%=60;
3173 if(time<10) ret+="0";
3174 ret+=time+"";
3175 return ret;
3176}
3177
3178
3179/* Ein Befehl zum anschauen der Idlezeit anderer Spieler */
3180static int idlezeit(string player)
3181{
3182
3183 object player_ob;
3184 int idle;
3185
3186 if ( !player || player=="" ||
3187 player==lowerstring(this_player()->QueryProp(P_NAME)))
3188 {
3189 write ("Du bist selber natuerlich gerade nicht idle.\n");
3190 return 1;
3191 }
3192
3193 // Welcher Spieler ist gemeint?
3194 player_ob=find_player(player);
3195
3196 // Spieler nicht da oder Invis und Anfrager is kein Magier
3197 if (!player_ob ||
3198 (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
3199 {
3200 write(capitalize(player)+" ist gerade nicht im Spiel.\n");
3201 return 1;
3202 }
3203
3204 idle=query_idle(player_ob);
3205
3206 // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
3207 write (player_ob->QueryProp(P_NAME)+" ist "+
3208 (idle>=60?timediff(idle):"nicht")+" passiv.\n");
3209
3210 return 1;
3211}
3212
3213
3214/** Belebt einen netztoten Spieler wieder.
3215 * Reaktiviert Heartbeats, bewegt den Spieler zurueck an den Ort, der eim
3216 * Einschlafen zum Aufwachen ermittelt wurde (im einfachsten Fall der Raum,
3217 * wo man eingeschlafen ist).
3218 */
3219static void ndead_revive()
3220{
3221 string fname;
3222 int ret;
3223
3224 set_heart_beat(1);
3225 ndead_next_check = NETDEAD_CHECK_TIME;
3226 ndead_currently = 0;
3227 ndead_lasttime = 0;
3228
3229 if ( !objectp(ndead_location) &&
3230 stringp(ndead_l_filename) && sizeof(ndead_l_filename)) {
3231
3232 if ( member( ndead_l_filename, '#' ) == -1 ){
3233 catch(load_object( ndead_l_filename); publish);
3234 ndead_location = find_object(ndead_l_filename);
3235 }
3236 else {
3237 ndead_location = find_object(ndead_l_filename);
3238 if ( !ndead_location && env_ndead_info ){
3239 fname = explode( ndead_l_filename, "#" )[0];
3240 catch(ndead_location =
3241 (load_object(fname)->SetProp(P_NETDEAD_INFO, env_ndead_info));
3242 publish);
3243 if ( !objectp(ndead_location) ){
3244 catch(load_object( ndead_location);publish);
3245 ndead_location = find_object(ndead_location);
3246 }
3247 }
3248 }
3249 }
3250
3251 if ( !objectp(ndead_location)
3252 || catch(ret = move( ndead_location, M_GO|M_SILENT );publish)
3253 || ret != 1 ) {
3254 move( "gilden/abenteurer", M_GO|M_SILENT );
3255 ndead_location = environment();
3256 }
3257
3258 // ndead_location=0;
3259 ndead_l_filename = 0;
3260 env_ndead_info = 0;
3261}
3262
3263/** Bewegt einen netztoten Spieler in den Netztotenraum
3264 * Gerufen von heartbeat().
3265 * Zerstoert Gaeste, verlaesst ggf. das Team, ermittelt, ob der Spieler beim
3266 * Aufwachen in das alte Environment bewegt werden soll oder in einen anderen
3267 * Raum, hierzu wird im Environment P_NETDEAD_INFO abgefragt.
3268 * Deaktiviert die Kommandos per disable_commands().
3269 */
3270static void ndead_move_me() {
3271 object team;
3272 mixed amem;
3273
3274 set_heart_beat(0);
3275 stop_heart_beats();
3276 if (QueryGuest()) {
3277 quit();
3278 if (ME)
3279 remove();
3280 if (ME)
3281 destruct(ME);
3282 return;
3283 }
3284 ndead_next_check=NETDEAD_CHECK_TIME;
3285 ndead_currently=1;
3286 ndead_lasttime=0;
3287 ndead_location=environment();
3288 if (objectp(ndead_location))
3289 ndead_l_filename=object_name(ndead_location);
3290
3291 if (objectp(environment())
3292 && sizeof(explode(object_name(environment()),"#")) > 1)
3293 env_ndead_info=environment()->QueryProp(P_NETDEAD_INFO);
3294 else
3295 env_ndead_info=0;
3296
3297 if ( objectp(team=Query(P_TEAM)) )
3298 // Der Test auf assoziierte Teammitglieder (== FolgeNPCs)
3299 // verhindert, dass Spieler nach "schlafe ein" aus dem
3300 // Team ausgetragen werden. -- 29.01.2002 Tiamak
3301 // && !objectp(amem=Query(P_TEAM_ASSOC_MEMBERS))
3302 // && !(pointerp(amem) && sizeof(amem)))
3303 team->RemoveMember(ME);
3304
3305 disable_commands();
3306 move(NETDEAD_ROOM,M_GO|M_NO_ATTACK|M_NOCHECK,"ins Reich der Netztoten");
3307}
3308
3309/** Ist dieser Character ein Gast?
3310 * @return 1, wenn Gast, 0 sonst.
3311 */
3312int QueryGuest()
3313{
3314 string dummy;
3315 return sscanf(getuid(),"gast%d",dummy);
3316}
3317
3318/** Spielerkommando 'schlafe ein'.
3319 * Ruft remove_interactive() bei Spielern, bei Magiern wird quit() gerufen,
3320 * um das Magierobjekt zu zerstoeren.
3321 * @sa quit()
3322 */
3323int disconnect(string str)
3324{
3325 string verb;
3326 string desc = break_string(
3327 "\"schlafe ein\" beendet Deine Verbindung mit "MUDNAME". Du behaeltst "
3328 "Deine Sachen.\nFalls "MUDNAME" jedoch abstuerzt oder neu gestartet "
3329 "wird, waehrend Du weg bist, verlierst Du die meisten allerdings "
3330 "(genauso, als wenn Du Deine Verbindung mit \"ende\" beendet haettest). "
3331 "In diesem Fall bekommst Du dann eine kleine Entschaedigung."
3332 ,78,0,BS_LEAVE_MY_LFS);
3333
3334 verb=query_verb();
3335 if (!verb)
3336 verb="AUTOCALL";
3337 if (verb[0..5]=="schlaf" && str!="ein")
3338 {
3339 notify_fail(desc);
3340 return 0;
3341 }
3342 if (IS_LEARNER(this_object()))
3343 return quit();
3344
3345 tell_object(this_object(), desc);
3346
3347 if (clonep(environment()) && !environment()->QueryProp(P_NETDEAD_INFO))
3348 tell_object(this_object(),break_string(
3349 "\nACHTUNG: Wenn Du hier laengere Zeit schlaefst, "
3350 "kommst Du vermutlich nicht an diesen Ort zurueck!",78));
3351
3352 say(capitalize(name(WER))+" hat gerade die Verbindung zu "MUDNAME" gekappt.\n");
3353 remove_interactive(ME);
3354 call_out(#'clear_tell_history,4);
3355 return 1;
3356}
3357
3358static int finger (string str)
3359{
3360 string ret;
3361 mixed xname;
3362
3363 if (!str || str==""
3364 || sizeof(explode(str," ")-({"-n","-p","-s","-v","-a"}))>1)
3365 {
3366 write("finger <spielername> oder finger <spielername@mudname>\n"+
3367 "Bitte nur den reinen Spielernamen verwenden, keine Namensvorsaetze oder Titel\n");
3368 return 1;
3369 }
3370 xname=explode(str,"@");
3371 if(sizeof(xname)==2)
3372 {
3373 if (xname[0]=="-n " || xname[0]=="-p " || xname[0]=="-s ") {
3374 write("finger <spielername>@<mudname> - der Spielername fehlt.\n");
3375 return 1;
3376 }
3377 if (ret=INETD->_send_udp(xname[1],([
3378 REQUEST: "finger",
3379 SENDER: getuid(ME),
3380 DATA: (explode(xname[0]," ")-({"-n","-p","-s"}))[0]
3381 ]), 1))
3382 write(ret);
3383 else
3384 write("Anfrage abgeschickt.\n");
3385 return 1;
3386 }
3387 "/p/daemon/finger"->finger_single(str,1);
3388 return 1;
3389}
3390
3391string lalign(string str, int wid)
3392{
3393 return (str+" "+
3394 " ")[0..wid-1];
3395}
3396
3397#define MUDS_BAR "\
3398-------------------------------------------------------------------------------"
3399
3400private void format(mixed mud, mixed hosts, string output)
3401{
3402 output += lalign(hosts[mud][HOST_NAME], 20) + " " +
3403 (hosts[mud][HOST_STATUS] ?
3404 hosts[mud][HOST_STATUS] > 0 ?
3405 "UP " + ctime(hosts[mud][HOST_STATUS])[4..15] :
3406 "DOWN " + ctime(-hosts[mud][HOST_STATUS])[4..15]
3407 : "UNKNOWN Never accessed.") + "\n";
3408}
3409
3410static int muds() {
3411 mapping hosts;
3412 int i;
3413 mixed muds, output;
3414
3415 output = lalign("Mudname", 20) + " Status Last access";
3416 output += "\n" + MUDS_BAR[0..sizeof(output)] + "\n";
3417 muds = sort_array(m_indices(hosts = INETD->query("hosts")),#'>);
3418 map(muds, #'format, hosts, &output);
3419 More(output);
3420 return 1;
3421}
3422
3423// **** local property methods
3424static int _set_level(int i)
3425{
3426 if (!intp(i)) return -1;
3427 if (i<1) return -1;
3428 Set(P_LEVEL, i);
3429 GMCP_Char( ([P_LEVEL: i]) );
3430 return i;
3431}
3432
3433static int _set_invis(int a)
3434{
3435 return Set(P_INVIS, intp(a) ? a : !Query(P_INVIS));
3436}
3437
3438/* sets the terminal type */
3439/* note: support vt100 (b/w), ansi (color), dumb (none) */
3440static string _set_tty(string str) {
3441 if(str != "dumb" && str != "vt100" && str != "ansi")
3442 return Query(P_TTY);
3443 return Set(P_TTY, str);
3444}
3445
3446static int stty(string str)
3447{
3448 if(str!="dumb"&&str!="vt100"&&str!="ansi"&&str!="reset")
3449 {
3450 write("Kommando: stty dumb|vt100|ansi oder reset\n");
3451 }
3452 if(str == "reset") {
3453 printf("Dieser Text sollte lesbar sein!\n");
3454 return 1;
3455 }
3456
3457 write("TTY steht jetzt auf "+SetProp(P_TTY,str)+".\n");
3458 if(str == "ansi" || str == "vt100") {
3459 printf("Terminal Test:\n");
3460 printf("VT100: fett unterstrichen "+
3461 "blinkend invers\n");
3462 if(str == "ansi") {
3463 printf("ANSI Farben und VT100 Attribute:\n");
3464 foreach(int fg: 30 .. 37) {
3465 foreach(int bg: 40 .. 47) {
3466 printf("[%d;%dm@", fg, bg);
3467 printf("[%d;%dm@", fg, bg);
3468 printf("[%d;%dm@", fg, bg);
3469 printf("[%d;%dm@", fg, bg);
3470 }
3471 printf("\n");
3472 }
3473 printf("Sollte dieser Text hier nicht richtig lesbar\nsein,"+
3474 "benutze das Kommando stty reset!\n");
3475 }
3476
3477 }
3478 return 1;
3479}
3480
3481int set_ascii_art(string str)
3482{
3483 if (str!="ein"&&str!="aus")
3484 {
3485 printf("Du moechtest 'Grafik' "+(QueryProp(P_NO_ASCII_ART)?"NICHT ":"")+
3486 "sehen.\n");
3487 }
3488
3489 if (str=="ein") {
3490 SetProp(P_NO_ASCII_ART, 0);
3491 printf("Zukuenftig moechtest Du 'Grafik' sehen.\n");
3492 }
3493
3494 if (str=="aus") {
3495 SetProp(P_NO_ASCII_ART, 1);
3496 printf("Zukuenftig moechtest Du KEINE 'Grafik' mehr sehen.\n");
3497 }
3498
3499
3500 return 1;
3501}
3502
3503int _set_shell_version(int arg)
3504{
3505 if (!intp(arg))
3506 return -1;
3507 Set(P_SHELL_VERSION,({QueryProp(P_RACE),arg}));
3508 return 1;
3509}
3510
3511int _query_shell_version()
3512{ mixed sv;
3513
3514 if (!(sv=Query(P_SHELL_VERSION)) || !pointerp(sv) || sizeof(sv)!=2 ||
3515 sv[0]!=QueryProp(P_RACE) || !intp(sv[1]))
3516 return 0;
3517 return sv[1];
3518}
3519
3520// XxXxXxXxXx
3521
3522int more(string str)
3523{
3524 if(!str)
3525 {
3526 notify_fail("Usage: more <file>\n");
3527 return 0;
3528 }
3529 if (file_size(str) <= 0) {
3530 notify_fail(str+": No such file\n");
3531 return 0;
3532 }
3533 More(str, 1);
3534 return 1;
3535}
3536
3537static int set_visualbell(string str)
3538{
3539 if(!str)
3540 {
3541 write("Derzeitige Einstellung fuer Tonausgabe: "+
3542 (QueryProp(P_VISUALBELL)?"AUS":"EIN")+".\n");
3543 return 1;
3544 }
3545 if (str=="ein")
3546 {
3547 if(!QueryProp(P_VISUALBELL))
3548 write("Die Tonausgabe stand schon auf EIN.\n");
3549 else
3550 {
3551 SetProp(P_VISUALBELL,0);
3552 write("OK, Tonausgabe auf EIN gestellt.\n");
3553 }
3554 }
3555 else
3556 if (str=="aus")
3557 {
3558 if(QueryProp(P_VISUALBELL))
3559 write("Die Tonausgabe stand schon auf AUS.\n");
3560 else
3561 {
3562 SetProp(P_VISUALBELL,1);
3563 write("OK, Tonausgabe auf AUS gestellt.\n");
3564 }
3565 }
3566 else
3567 write("Syntax: ton [ein|aus]\n");
3568 return 1;
3569}
3570
3571static int set_screensize(string str)
3572{
3573 int size;
3574
3575 if (str && (str[0..2] == "abs" || str[0..2]=="rel")) {
3576 size = QueryProp(P_MORE_FLAGS);
3577 if (str[0..2] == "abs") {
3578 size |= E_ABS;
3579 write("Es wird beim Prompt die Zeilenzahl des Textes angegeben.\n");
3580 }
3581 else {
3582 size &= ~E_ABS;
3583 write("Es wird beim Prompt der prozentuale Anteil des Textes angegeben.\n");
3584 }
3585 SetProp(P_MORE_FLAGS, size);
3586 return 1;
3587 }
3588
3589 if ( str && (str=="auto" || sscanf( str, "auto %d", size )) ){
3590 if ( size > 0 ){
3591 write("Ungueltiger Wert! "
3592 "In Verbindung mit 'auto' sind nur negative Werte erlaubt.\n");
3593 return 1;
3594 }
3595
3596 SetProp( P_SCREENSIZE, size-1 );
3597
3598 write("Ok, Deine Zeilenzahl wird nun automatisch ermittelt (derzeit "+
3599 QueryProp(P_SCREENSIZE)+").\n"+
3600 break_string("Bitte beachte, dass dies nur einwandfrei "
3601 "funktioniert, wenn Dein Client Telnetnegotiations "
3602 "unterstuetzt (siehe auch \"hilfe telnegs\").") );
3603 return 1;
3604 }
3605
3606 if ( !str || str=="" || !sscanf( str, "%d", size ) || size < 0 || size > 100){
3607 write(break_string(
3608 sprintf("Mit dem Befehl 'zeilen <groesse>' kannst Du einstellen, "
3609 "wieviele Zeilen bei mehrseitigen Texten auf einmal ausgegeben "
3610 "werden. Die angegebene Groesse muss zwischen 0 und 100 liegen. "
3611 "Bei Groesse 0 wird einfach alles ausgegeben (ohne Pause). Mit "
3612 "der Einstellung 'auto' wird die Groesse automatisch ueber "
3613 "die Telnetnegotiations ermittelt (siehe auch 'hilfe telnegs'). "
3614 "Um nach einer Seite Text noch etwas Platz zu haben, kann man z.B. "
3615 "'zeilen auto -3' einstellen.\n"
3616 "Die Voreinstellung ist 20 Zeilen.\n"
3617 "Mit 'zeilen abs[olut]' und 'zeilen rel[ativ]' kannst Du fest"
3618 "legen, ob im Prompt bei langen Texten die aktuelle Zeilennummer "
3619 "oder eine prozentuale Angabe ausgegeben wird.\n"
3620 "Deine aktuelle Einstellung ist %d%s Zeilen (%s).",
3621 QueryProp(P_SCREENSIZE),
3622 Query(P_SCREENSIZE) < 0 ? " 'automatische'" : "",
3623 QueryProp(P_MORE_FLAGS) & E_ABS ? "absolut" : "relativ"),78,0,1));
3624 return 1;
3625 }
3626
3627 SetProp( P_SCREENSIZE, size );
3628
3629 printf( "Okay, Deine Zeilenzahl steht nun auf %d.\n", size );
3630 return 1;
3631}
3632
3633static int _query_screensize()
3634{
3635 int sz,rows;
3636
3637 if ( (sz=Query(P_SCREENSIZE)) >= 0 )
3638 return sz;
3639
3640 if ( !rows=QueryProp(P_TTY_ROWS) )
3641 return 0;
3642
3643 return (rows+=sz) >= 5 ? rows : 5;
3644}
3645
3646static int presay(string str)
3647{
3648 if (!str=_unparsed_args())
3649 write("Dein Presay ist jetzt geloescht.\n");
3650 else
3651 printf("Dein Presay lautet jetzt: \"%s\".\n",str=capitalize(str));
3652 SetProp(P_PRESAY,str);
3653 return 1;
3654}
3655
3656static int sethands(string str)
3657{
3658 mixed *hands;
3659
3660 if (!(str=_unparsed_args()))
3661 {
3662 write("sethands <message>\n");
3663 return 1;
3664 }
3665 if (str=="0")
3666 hands=RaceDefault(P_HANDS);
3667 if (!hands || !pointerp(hands))
3668 hands=Query(P_HANDS);
3669 hands[0]=" "+str;
3670 Set(P_HANDS,hands);
3671 write("Ok.\n");
3672 return 1;
3673}
3674
3675static int inform(string str)
3676{
3677 switch (str) {
3678 case "on":
3679 case "ein":
3680 case "an":
3681 if (Query(P_INFORMME))
3682 write("Das hattest Du schon so eingestellt.\n");
3683 else
3684 {
3685 write("Kuenftig wirst Du informiert, wenn jemand das "MUDNAME" verlaesst/betritt.\n");
3686 Set(P_INFORMME,1);
3687 }
3688 return 1;
3689 case "aus":
3690 case "off":
3691 if (!Query(P_INFORMME))
3692 write("Das hattest Du schon so eingestellt.\n");
3693 else
3694 {
3695 write("Ok.\n");
3696 Set(P_INFORMME,0);
3697 }
3698 return 1;
3699 case 0:
3700 write("Inform-Mode ist "+(Query(P_INFORMME)?"an":"aus")+"geschaltet.\n");
3701 return 1;
3702 }
3703 write("inform an oder inform aus, bitte.\n");
3704 return 1;
3705}
3706
3707void delayed_write(mixed *what)
3708{
3709 if (!pointerp(what)||!sizeof(what)||!pointerp(what[0]))
3710 return;
3711 tell_object(ME,what[0][0]);
3712 if (sizeof(what)>1&&sizeof(what[0])>1)
3713 call_out("delayed_write",what[0][1],what[1..]);
3714}
3715
3716void notify_player_change(string who, int rein, int invis)
3717{
3718 string *list,name;
3719 mixed mlist;
3720
3721 if (invis) name="("+who+")";
3722 else name=who;
3723
3724 if (Query(P_INFORMME))
3725 {
3726 if (rein)
3727 tell_object(ME,name+" ist gerade ins "MUDNAME" gekommen.\n");
3728 else
3729 tell_object(ME,name+" hat gerade das "MUDNAME" verlassen.\n");
3730 }
3731
3732 if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
3733
3734 if(pointerp(list=Query(P_WAITFOR)) && sizeof(list) && member(list,who)!=-1)
3735 {
3736 if (!QueryProp(P_VISUALBELL))
3737 name+=sprintf("%c",7); // Char fuer Pieps an den String anhaengen.
3738 // Moechte der Spieler keine ASCII-Grafik sehen, wird diese Meldung ohne
3739 // Leerzeichen formatiert, so dass sie von Screenreadern vorgelesen wird.
3740 // Anderenfalls wuerde sie einzeln buchstabiert.
3741 if ( QueryProp(P_NO_ASCII_ART) )
3742 {
3743 delayed_write( ({ ({ sprintf("%s IST JETZT %sDA !!!\n",
3744 name, (rein?"":"NICHT MEHR ")) }) }) );
3745 }
3746 else
3747 {
3748 delayed_write( ({ ({ sprintf("%s I S T J E T Z T %sD A !!!\n",
3749 name, (rein?"":"N I C H T M E H R ")) }) }) );
3750 }
3751 }
3752
3753 if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
3754 (mappingp(mlist)) && (mlist[who]))
3755 Show_WaitFor_Reason(who,invis);
3756}
3757
3758static int erwarte(string str)
3759{
3760 string *list,*str1;
3761 mixed mlist;
3762 int i;
3763
3764 if (!mappingp(mlist=QueryProp(P_WAITFOR_REASON)))
3765 mlist=([]);
3766 if (!pointerp(list=Query(P_WAITFOR)))
3767 list=({});
3768
3769 if (!str || str=="-u")
3770 {
3771 if(Query(P_WAITFOR_FLAGS)&0x01)
3772 write("Du hast 'erwarte' temporaer deaktiviert.\n");
3773 write("Du erwartest jetzt");
3774 if (!sizeof(list))
3775 write(" niemanden mehr.\n");
3776 else
3777 {
3778 write(":\n");
3779 if (!str) list=sort_array(list,#'>);
3780 More(break_string(CountUp(list),78));
3781 }
3782 return 1;
3783 }
3784 if(str=="aus"){
3785 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)|0x01);
3786 write("Erwarte ist jetzt deaktiviert.\n");
3787 return 1;
3788 }
3789 if(str=="an" || str=="ein"){
3790 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)&0xFE);
3791 write("Erwarte ist jetzt aktiv.\n");
3792 return 1;
3793 }
3794
3795 str1=explode(_unparsed_args()||""," ");
3796 if (sizeof(str1)==1)
3797 {
3798 if (str1[0]!="wegen")
3799 {
3800 str=capitalize(lower_case(str));
3801 if (member(list,str)!=-1)
3802 {
3803 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,str));
3804 list-=({str});
3805 write(str+" aus der Liste entfernt.\n");
3806 } else
3807 {
3808 if (sizeof(list)>1000)
3809 {
3810 write("Du erwartest schon genuegend!\n");
3811 return 1;
3812 }
3813 list+=({str});
3814 write(str+" an die Liste angehaengt.\n");
3815 }
3816 Set(P_WAITFOR,list);
3817 }
3818 else
3819 {
3820 if (sizeof(mlist) && sizeof(list=m_indices(mlist)))
3821 {
3822 write("Du erwartest aus einem bestimmten Grund:\n");
3823 write(break_string(CountUp(sort_array(list,#'>))+".",78));
3824 }
3825 else write("Du erwartest niemanden aus einem bestimmten Grund.\n");
3826 }
3827 return 1;
3828 }
3829 notify_fail("Falsche Syntax, siehe 'hilfe erwarte'!\n");
3830 if (str1[1]!="wegen") return 0;
3831 if (sizeof(str1)==2)
3832 Show_WaitFor_Reason(capitalize(lower_case(str1[0])),0);
3833 else {
3834 string s=capitalize(lower_case(str1[0]));
3835 if (sizeof(str1)==3 && (str1[2]=="nichts" || str1[2]=="loeschen"))
3836 if (!mlist[s])
3837 write("Du hast "+s+" aus keinem bestimmten Grund erwartet!\n");
3838 else
3839 {
3840 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,s));
3841 write("Du erwartest "+s+" aus keinem bestimmten Grund mehr!\n");
3842 }
3843 else
3844 {
3845 if (IS_ARCH(ME)) i=80; else if (IS_LEARNER(ME)) i=40;
3846 else if (IS_SEER(ME)) i=20; else i=10;
3847 if (!mlist[s] && sizeof(mlist)>=i)
3848 write("Sorry, aber Du erwartest schon genuegend Leute!\n");
3849 else
3850 {
3851 SetProp(P_WAITFOR_REASON,mlist+([s:implode(str1[2..]," ")]));
3852 Show_WaitFor_Reason(s,0);
3853 }
3854 }
3855 }
3856 return 1;
3857}
3858
3859static int uhrmeldung(string str)
3860{
3861 if (!(str=_unparsed_args()))
3862 {
3863 str=QueryProp(P_CLOCKMSG);
3864 if (!str)
3865 {
3866 write("Du hast die Standard-Uhrmeldung.\n");
3867 return 1;
3868 }
3869 if( !stringp(str) ) str = sprintf("%O\n",str);
3870 printf("Deine Uhrmeldung ist:\n%s\n",str[0..<2]);
3871 return 1;
3872 }
3873 if (str=="0")
3874 {
3875 SetProp(P_CLOCKMSG,0);
3876 write("Ok, Du hast jetzt wieder die Standard-Meldung.\n");
3877 return 1;
3878 }
3879 if (sizeof(explode(str,"%d"))>2)
3880 {
3881 write("Fehler, es darf nur ein %d in der Meldung vorkommen.\n");
3882 return 1;
3883 }
3884 /* Mehrere %-Parameter verursachen das Abschalten der Uhr zur vollen Stunde.
3885 */
3886 if (sizeof(explode(str,"%"))>2)
3887 {
3888 write("Fehler: Zuviele %-Parameter in der Meldung.\n");
3889 return 1;
3890 }
3891 /* Nur ein %-Parameter, aber der falsche: nicht sinnvoll. */
3892 else
3893 {
3894 int i = strstr(str,"%",0);
3895 if ( i>-1 && ( i==sizeof(str)-1 || str[i+1]!='d'))
3896 {
3897 write("Fehler: Falscher %-Parameter in der Meldung.\n");
3898 return 1;
3899 }
3900 }
3901 str+="\n";
3902 SetProp(P_CLOCKMSG,str);
3903 write("Ok.\n");
3904 return 1;
3905}
3906
3907static int zeitzone(string str)
3908{
3909 int zt;
3910 if(!str || str==""){
3911 if(!(zt=QueryProp(P_TIMEZONE)))
3912 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
3913 "eingestellt.\n");
3914 else if(zt>0)
3915 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
3916 "eingestellt.\n",zt);
3917 else
3918 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
3919 "Berlin eingestellt.\n",-zt);
3920 return 1;
3921 }
3922 if(sscanf(str,"utc %d",zt)==1) zt=(zt-1)%24;
3923 else zt=to_int(str)%24;
3924
3925 SetProp(P_TIMEZONE,zt);
3926
3927 if(!zt)
3928 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
3929 "eingestellt.\n");
3930 else if(zt>0)
3931 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
3932 "eingestellt.\n",zt);
3933 else
3934 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
3935 "Berlin eingestellt.\n",-zt);
3936 return 1;
3937}
3938
3939static int emailanzeige(string str){
3940 notify_fail("Syntax: emailanzeige [alle|freunde|niemand]\n");
3941 if(!str || str==""){
3942 if(!(str=QueryProp(P_SHOWEMAIL)))str="Niemandem";
3943 else if(str=="alle")str="allen";
3944 else if(str=="freunde")str="Deinen Freunden";
3945 else if(str=="niemand")str="niemandem";
3946 else{
3947 SetProp(P_SHOWEMAIL,0);
3948 str="Niemandem";
3949 }
3950 write("Deine Email wird "+str+" angezeigt.\n");
3951 return 1;
3952 }
3953 else if(member(({"alle","freunde","niemand"}),str)==-1)return 0;
3954
3955 SetProp(P_SHOWEMAIL,str);
3956
3957 if(str=="alle")str="allen";
3958 else if(str=="freunde")str="Deinen Freunden";
3959 else str="niemandem";
3960 write("Deine Email wird "+str+" angezeigt.\n");
3961 return 1;
3962}
3963
3964static int zaubertraenke()
3965{
3966 More("/room/orakel"->TipListe());
3967 return 1;
3968}
3969
3970varargs static int angriffsmeldung(string arg) {
3971 if (arg=="ein" || arg=="an")
3972 SetProp(P_SHOW_ATTACK_MSG,1);
3973 else if (arg=="aus")
3974 SetProp(P_SHOW_ATTACK_MSG,0);
3975 if (QueryProp(P_SHOW_ATTACK_MSG))
3976 write("Du siehst saemtliche Angriffsmeldungen von Dir.\n");
3977 else
3978 write("Du siehst nur neue Angriffsmeldungen von Dir.\n");
3979 return 1;
3980}
3981
3982static mixed _query_localcmds()
3983{
3984 return ({({"zeilen","set_screensize",0,0}),
3985 ({"email","set_email",0,0}),
3986 ({"url","set_homepage",0,0}),
3987 ({"icq","set_icq",0,0}),
3988 ({"messenger", "set_messenger", 0, 0}),
3989 ({"ort","set_location",0,0}),
3990 ({"punkte","short_score",0,0}),
3991 ({"score","short_score",0,0}),
3992 ({"info","score",0,0}),
3993 ({"kurzinfo","very_short_score",0,0}),
3994 ({"quit","new_quit",0,0}),
3995 ({"ende","new_quit",0,0}),
3996 ({"disconnect","disconnect",0,0}),
3997 ({"schlaf","disconnect",1,0}),
3998 ({"speichern","save_character",0,0}),
3999 ({"save","save_character",0,0}),
4000 ({"toete","kill",0,0}),
4001 ({"angriffsmeldung","angriffsmeldung",0,0}),
4002 ({"passw","change_password",1,0}),
4003 ({"hilfe","help",1,0}),
4004 ({"selbstloeschung","self_delete",0,0}),
4005 ({"spielpause","spielpause",0,0}),
4006 ({"spieldauer","spieldauer",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004007 ({"idee","ReportError",0,0}),
4008 ({"typo","ReportError",0,0}),
4009 ({"bug","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004010 ({"fehler","fehlerhilfe",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004011 ({"md","ReportError",0,0}),
4012 ({"detail","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004013 ({"vorsicht","toggle_whimpy",0,0}),
4014 ({"stop","stop",0,0}),
4015 ({"kwho","kwho",0,0}),
4016 ({"kwer","kwho",0,0}),
4017 ({"kkwer","kkwho",0,0}),
4018 ({"kkwho","kkwho",0,0}),
4019 ({"who","who",0,0}),
4020 ({"wer","who",0,0}),
4021 ({"zeit","uhrzeit",0,0}),
4022 ({"uhrzeit","uhrzeit",0,0}),
4023 ({"weg","weg",0,0}),
4024 ({"wegmeldung", "wegmeldung", 0, 0}),
4025 ({"idlezeit", "idlezeit", 0, 0}),
4026 ({"finger","finger",0,0}),
4027 ({"muds","muds",0,0}),
4028 ({"emote","emote",0,0}),
4029 ({":","emote",1,0}),
4030 ({";","emote",1,0}),
4031 ({"remote","remote",0,SEER_LVL}),
4032 ({"r:","remote",1,0}),
4033 ({"r;","gremote",1,0}),
4034 ({"titel","set_title",0,0}),
4035 ({"review","review",0,SEER_LVL}),
4036 ({"setmin","setmin",0,SEER_LVL}),
4037 ({"setmout","setmout",0,SEER_LVL}),
4038 ({"setmmin","setmmin",0,SEER_LVL}),
4039 ({"setmmout","setmmout",0,SEER_LVL}),
4040 ({"sethands","sethands",0,SEER_LVL}),
4041 ({"presay","presay",0,SEER_LVL}),
4042 ({"extralook","extralook",0,SEER_LVL}),
4043 ({"fluchtrichtung","toggle_whimpy_dir",0,SEER_LVL}),
4044 ({"inform","inform",0,0}),
4045 ({"erwarte","erwarte",0,0}),
4046 ({"stty","stty",0,0}),
4047 ({"grafik", "set_ascii_art", 0, 0}),
4048 ({"uhrmeldung","uhrmeldung",0,0}),
4049 ({"zeitzone","zeitzone",0,0}),
4050 ({"behalte","behalte",0,0}),
4051 ({"zweitiemarkierung","zweitiemarkierung",0,0}),
4052 ({"emailanzeige","emailanzeige",0,0}),
4053 ({"topliste","topliste",0,0}),
4054 ({"ton","set_visualbell",0,0}),
4055 ({"telnegs","show_telnegs",0,0}),
4056 ({"spotte", "spotte", 0, 0}),
4057 ({"reise","reise",0,0}),
4058 ({"zaubertraenke","zaubertraenke",0,0}),
4059 ({"telnet","telnet_cmd",0,0}),
4060 })+
4061 command::_query_localcmds()+
4062 viewcmd::_query_localcmds()+
4063 comm::_query_localcmds()+
4064 skills::_query_localcmds()+
4065 description::_query_localcmds();
4066}
4067
4068static int _check_keep(object ob)
4069{
4070 return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
4071}
4072
4073static mixed _set_testplayer(mixed arg) {
4074 mixed res;
4075 object setob;
4076
4077 setob=this_player();
4078 if (!objectp(setob) || !query_once_interactive(setob))
4079 setob=this_interactive();
4080 if (!objectp(setob))
4081 setob=previous_object();
4082 if (setob && !IS_DEPUTY(setob)) {
4083 arg=geteuid(setob);
4084 if (!arg || arg=="NOBODY")
4085 arg=getuid(setob);
4086 arg=capitalize(arg);
4087 }
4088 res=Set(P_TESTPLAYER,arg);
4089 Set(P_TESTPLAYER,PROTECTED,F_MODE_AS);
4090 return res;
4091}
4092
4093int zweitiemarkierung(string arg)
4094{
4095 if (!QueryProp(P_SECOND))
4096 return _notify_fail("Aber Du bist doch gar kein Zweiti.\n"),0;
4097 notify_fail("Syntax: zweitiemarkierung [unsichtbar|sichtbar|name]\n");
4098 if (!arg)
4099 return 0;
4100 switch (arg)
4101 {
4102 case "unsichtbar" :
4103 SetProp(P_SECOND_MARK,-1);
4104 write("Jetzt sieht kein Spieler mehr, dass Du ein Zweiti bist.\n");
4105 return 1;
4106 case "sichtbar" :
4107 SetProp(P_SECOND_MARK,0);
4108 write("Jetzt sieht kein Spieler mehr, wessen Zweiti Du bist.\n");
4109 return 1;
4110 case "name" :
4111 SetProp(P_SECOND_MARK,1);
4112 write("Jetzt koennen alle sehen, wessen Zweiti Du bist.\n");
4113 return 1;
4114 }
4115 return 0;
4116}
4117
4118int topliste(string arg)
4119{
4120 if (!arg)
4121 {
4122 printf("Du hast Dich fuer die Topliste %s.\n",
4123 (QueryProp(P_NO_TOPLIST) ? "gesperrt" : "freigegeben"));
4124 return 1;
4125 }
4126 else if (member(({"j","ja","n","nein"}),arg)==-1)
4127 return _notify_fail("Syntax: topliste [ja|nein]\n"),0;
4128 if (arg[0]=='j')
4129 {
4130 SetProp(P_NO_TOPLIST,0);
4131 write("Du kannst jetzt (theoretisch) in der Topliste auftauchen.\n");
4132 }
4133 else
4134 {
4135 SetProp(P_NO_TOPLIST,1);
Zesstradd2d1982017-01-28 14:03:19 +01004136 "/secure/topliste"->DeletePlayer();
4137 write("Du wirst jetzt nicht (mehr) in den Toplisten auftauchen.\n");
MG Mud User88f12472016-06-24 23:31:02 +02004138 }
4139 Set(P_NO_TOPLIST,SAVE|PROTECTED,F_MODE_AS);
4140 return 1;
4141}
4142
4143int show_telnegs(string arg)
4144{
4145 if (!arg)
4146 {
4147 write("Du bekommst Aenderungen Deiner Fenstergroesse "+
4148 (QueryProp(P_TTY_SHOW)?"":"nicht ")+"angezeigt.\n");
4149 return 1;
4150 }
4151 if (member(({"ein","an","aus"}),arg)==-1)
4152 {
4153 write("Syntax: telnegs [ein|aus]\n");
4154 return 1;
4155 }
4156 if (arg=="ein" || arg=="an")
4157 {
4158 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"":"nun ")+
4159 "Aenderungen Deiner Fenstergroesse angezeigt.\n");
4160 Set(P_TTY_SHOW,1);
4161 return 1;
4162 }
4163 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"nun ":"")+
4164 "Aenderungen Deiner Fenstergroesse nicht "+
4165 (QueryProp(P_TTY_SHOW)?"mehr ":"")+"angezeigt.\n");
4166 Set(P_TTY_SHOW,0);
4167 return 1;
4168}
4169
4170private int set_keep_alive(string str) {
4171 if (str == "ein") {
4172 telnet_tm_counter = 240 / __HEART_BEAT_INTERVAL__;
4173 tell_object(this_object(), break_string(
4174 "An Deinen Client werden jetzt alle 4 Minuten unsichtbare Daten "
4175 "geschickt, um zu verhindern, dass Deine Verbindung zum "MUDNAME
4176 " beendet wird.", 78));
4177 }
4178 else if (str == "aus") {
4179 telnet_tm_counter = 0;
4180 tell_object(this_object(),break_string(
4181 "Du hast das Senden von unsichtbaren Daten (Keep-Alive-Pakete) an "
4182 "Deinen Client ausgeschaltet.",78));
4183 }
4184 else {
4185 if (!telnet_tm_counter)
4186 tell_object(this_object(), break_string(
4187 "An Deinen Client werden keine Keep-Alive-Pakete geschickt.",78));
4188 else
4189 tell_object(this_object(), break_string(
4190 "An Deinen Client werden alle 4 Minuten "
4191 "unsichtbare Daten geschickt, damit Deine Verbindung "
4192 "zum "MUDNAME" nicht beendet wird.",78));
4193 }
4194 return 1;
4195}
4196
4197private int print_telnet_rttime() {
4198 int rtt = QueryProp(P_TELNET_RTTIME);
4199 if (rtt>0)
4200 tell_object(ME, break_string(
4201 "Die letzte gemessene 'round-trip' Zeit vom MG zu Deinem Client "
4202 "und zurueck betrug " + rtt + " us.",78));
4203 else
4204 tell_object(ME, break_string(
4205 "Bislang wurde die 'round-trip' Zeit vom MG zu Deinem Client "
4206 "noch nicht gemessen oder Dein Client unterstuetzt dieses "
4207 "nicht.",78));
4208 return 1;
4209}
4210
4211int telnet_cmd(string str) {
4212 if (!str) return 0;
4213 string *args = explode(str, " ");
4214 string newargs;
4215 if (sizeof(args) > 1)
4216 newargs = implode(args[1..], " ");
4217 else
4218 newargs = "";
4219
4220 switch(args[0])
4221 {
4222 case "keepalive":
4223 return set_keep_alive(newargs);
4224 case "rttime":
4225 return print_telnet_rttime();
4226 }
4227 return 0;
4228}
4229
4230int spotte( string str )
4231{
4232 _notify_fail( "Hier ist nichts, was Du verspotten koenntest!\n" );
4233 return 0;
4234}
4235
4236int behalte(string str)
4237{
4238 object ob,*obs;
4239 string s;
4240
4241 if (str)
4242 {
4243 if (str=="alles") {
4244 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, getuid());
4245 write("Ok!\n");
4246 return 1;
4247 }
4248 if (str=="nichts") {
4249 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, 0);
4250 write("Ok!\n");
4251 return 1;
4252 }
4253 if (!sizeof(obs=find_obs(str,PUT_GET_NONE)))
4254 {
4255 _notify_fail("Aber sowas hast Du nicht dabei!\n");
4256 return 0;
4257 }
4258 else ob=obs[0];
4259
4260 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4261 ob->SetProp(P_KEEP_ON_SELL,0);
4262 else
4263 ob->SetProp(P_KEEP_ON_SELL,geteuid(ME));
4264
4265 // erneut abfragen, da sich der Wert nicht geaendert haben muss
4266 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4267 write(break_string(sprintf("Ok, Du wirst %s jetzt bei 'verkaufe alles' "
4268 "behalten.\n",ob->name(WEN)),78));
4269 else
4270 write(break_string(sprintf("Ok, Du wirst %s beim naechsten 'verkaufe "
4271 "alles' mitverkaufen!\n",ob->name(WEN)),78));
4272
4273 return 1;
4274 }
4275 s=make_invlist(ME,filter(all_inventory(ME),#'_check_keep)); //'));
4276 More(s);
4277 return 1;
4278}
4279
4280static int _query_lep()
4281{
4282 int val;
4283 val = LEPMASTER->QueryLEP();
4284 Set( P_LEP, val );
4285 return val;
4286}
4287
4288static mixed _set_fraternitasdonoarchmagorum(mixed arg)
4289{
4290 if (!intp(arg)) return -1;
4291
4292 if ((!previous_object(1)||object_name(previous_object(1))!=FAO_MASTER) &&
4293 (!this_interactive() || !IS_ARCH(this_interactive())))
4294 return -1;
4295
4296 if (!intp(arg)) return -1;
4297
4298 log_file("fao/P_FAO",sprintf("%s - %s P_FAO gesetzt auf %O\n",
4299 dtime(time()),query_real_name(),arg) );
4300 return Set(P_FAO,arg);
4301}
4302
4303nomask void set_realip(string str)
4304{
4305 if(previous_object() && strstr(object_name(previous_object()),"/secure")==0)
4306 {
4307 realip=str;
4308 }
4309}
4310
4311nomask string query_realip()
4312{
4313 return realip ? realip : "";
4314}
4315
4316mixed _query_netdead_env() {
4317 return ndead_location || ndead_l_filename;
4318}