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