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