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