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