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