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