blob: 7b3b42aae9b22d2930ff88543205f87b195a8716 [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);
Zesstra996bafe2022-02-14 22:42:39 +01003469 call_out(#'clear_tell_history, 4, 0);
MG Mud User88f12472016-06-24 23:31:02 +02003470 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);
Zesstra3a261e52022-02-10 14:00:31 +01003557 comm::set_colourmap(str);
MG Mud User88f12472016-06-24 23:31:02 +02003558 return Set(P_TTY, str);
3559}
3560
3561static int stty(string str)
3562{
3563 if(str!="dumb"&&str!="vt100"&&str!="ansi"&&str!="reset")
3564 {
3565 write("Kommando: stty dumb|vt100|ansi oder reset\n");
3566 }
3567 if(str == "reset") {
3568 printf("Dieser Text sollte lesbar sein!\n");
3569 return 1;
3570 }
3571
3572 write("TTY steht jetzt auf "+SetProp(P_TTY,str)+".\n");
3573 if(str == "ansi" || str == "vt100") {
3574 printf("Terminal Test:\n");
3575 printf("VT100: fett unterstrichen "+
3576 "blinkend invers\n");
3577 if(str == "ansi") {
3578 printf("ANSI Farben und VT100 Attribute:\n");
3579 foreach(int fg: 30 .. 37) {
3580 foreach(int bg: 40 .. 47) {
3581 printf("[%d;%dm@", fg, bg);
3582 printf("[%d;%dm@", fg, bg);
3583 printf("[%d;%dm@", fg, bg);
3584 printf("[%d;%dm@", fg, bg);
3585 }
3586 printf("\n");
3587 }
Zesstra37125992019-08-08 21:10:00 +02003588 printf("Sollte dieser Text hier nicht richtig lesbar\nsein, "+
3589 "benutze das Kommando 'stty reset'.\n");
MG Mud User88f12472016-06-24 23:31:02 +02003590 }
3591
3592 }
3593 return 1;
3594}
3595
3596int set_ascii_art(string str)
3597{
3598 if (str!="ein"&&str!="aus")
3599 {
3600 printf("Du moechtest 'Grafik' "+(QueryProp(P_NO_ASCII_ART)?"NICHT ":"")+
3601 "sehen.\n");
3602 }
3603
3604 if (str=="ein") {
3605 SetProp(P_NO_ASCII_ART, 0);
3606 printf("Zukuenftig moechtest Du 'Grafik' sehen.\n");
3607 }
3608
3609 if (str=="aus") {
3610 SetProp(P_NO_ASCII_ART, 1);
3611 printf("Zukuenftig moechtest Du KEINE 'Grafik' mehr sehen.\n");
3612 }
3613
3614
3615 return 1;
3616}
3617
3618int _set_shell_version(int arg)
3619{
3620 if (!intp(arg))
3621 return -1;
3622 Set(P_SHELL_VERSION,({QueryProp(P_RACE),arg}));
3623 return 1;
3624}
3625
3626int _query_shell_version()
3627{ mixed sv;
3628
3629 if (!(sv=Query(P_SHELL_VERSION)) || !pointerp(sv) || sizeof(sv)!=2 ||
3630 sv[0]!=QueryProp(P_RACE) || !intp(sv[1]))
3631 return 0;
3632 return sv[1];
3633}
3634
3635// XxXxXxXxXx
3636
3637int more(string str)
3638{
3639 if(!str)
3640 {
3641 notify_fail("Usage: more <file>\n");
3642 return 0;
3643 }
3644 if (file_size(str) <= 0) {
3645 notify_fail(str+": No such file\n");
3646 return 0;
3647 }
3648 More(str, 1);
3649 return 1;
3650}
3651
3652static int set_visualbell(string str)
3653{
3654 if(!str)
3655 {
3656 write("Derzeitige Einstellung fuer Tonausgabe: "+
Bugfix3afcb792022-01-21 22:32:42 +01003657 ((QueryProp(P_ALERT) & AL_NO_SOUND)?"AUS":"EIN")+".\n");
MG Mud User88f12472016-06-24 23:31:02 +02003658 return 1;
3659 }
3660 if (str=="ein")
3661 {
Bugfix3afcb792022-01-21 22:32:42 +01003662 if(!(QueryProp(P_ALERT) & AL_NO_SOUND))
MG Mud User88f12472016-06-24 23:31:02 +02003663 write("Die Tonausgabe stand schon auf EIN.\n");
3664 else
3665 {
Bugfix3afcb792022-01-21 22:32:42 +01003666 SetProp(P_ALERT, QueryProp(P_ALERT) & ~ AL_NO_SOUND);
MG Mud User88f12472016-06-24 23:31:02 +02003667 write("OK, Tonausgabe auf EIN gestellt.\n");
3668 }
3669 }
3670 else
3671 if (str=="aus")
3672 {
Bugfix3afcb792022-01-21 22:32:42 +01003673 if(QueryProp(P_ALERT) & AL_NO_SOUND)
MG Mud User88f12472016-06-24 23:31:02 +02003674 write("Die Tonausgabe stand schon auf AUS.\n");
3675 else
3676 {
Bugfix3afcb792022-01-21 22:32:42 +01003677 SetProp(P_ALERT, QueryProp(P_ALERT) | AL_NO_SOUND);
MG Mud User88f12472016-06-24 23:31:02 +02003678 write("OK, Tonausgabe auf AUS gestellt.\n");
3679 }
3680 }
3681 else
3682 write("Syntax: ton [ein|aus]\n");
3683 return 1;
3684}
3685
3686static int set_screensize(string str)
3687{
3688 int size;
3689
3690 if (str && (str[0..2] == "abs" || str[0..2]=="rel")) {
3691 size = QueryProp(P_MORE_FLAGS);
3692 if (str[0..2] == "abs") {
3693 size |= E_ABS;
3694 write("Es wird beim Prompt die Zeilenzahl des Textes angegeben.\n");
3695 }
3696 else {
3697 size &= ~E_ABS;
3698 write("Es wird beim Prompt der prozentuale Anteil des Textes angegeben.\n");
3699 }
3700 SetProp(P_MORE_FLAGS, size);
3701 return 1;
3702 }
3703
3704 if ( str && (str=="auto" || sscanf( str, "auto %d", size )) ){
3705 if ( size > 0 ){
3706 write("Ungueltiger Wert! "
3707 "In Verbindung mit 'auto' sind nur negative Werte erlaubt.\n");
3708 return 1;
3709 }
3710
3711 SetProp( P_SCREENSIZE, size-1 );
3712
3713 write("Ok, Deine Zeilenzahl wird nun automatisch ermittelt (derzeit "+
3714 QueryProp(P_SCREENSIZE)+").\n"+
3715 break_string("Bitte beachte, dass dies nur einwandfrei "
3716 "funktioniert, wenn Dein Client Telnetnegotiations "
3717 "unterstuetzt (siehe auch \"hilfe telnegs\").") );
3718 return 1;
3719 }
3720
3721 if ( !str || str=="" || !sscanf( str, "%d", size ) || size < 0 || size > 100){
3722 write(break_string(
3723 sprintf("Mit dem Befehl 'zeilen <groesse>' kannst Du einstellen, "
3724 "wieviele Zeilen bei mehrseitigen Texten auf einmal ausgegeben "
3725 "werden. Die angegebene Groesse muss zwischen 0 und 100 liegen. "
3726 "Bei Groesse 0 wird einfach alles ausgegeben (ohne Pause). Mit "
3727 "der Einstellung 'auto' wird die Groesse automatisch ueber "
3728 "die Telnetnegotiations ermittelt (siehe auch 'hilfe telnegs'). "
3729 "Um nach einer Seite Text noch etwas Platz zu haben, kann man z.B. "
3730 "'zeilen auto -3' einstellen.\n"
3731 "Die Voreinstellung ist 20 Zeilen.\n"
3732 "Mit 'zeilen abs[olut]' und 'zeilen rel[ativ]' kannst Du fest"
3733 "legen, ob im Prompt bei langen Texten die aktuelle Zeilennummer "
3734 "oder eine prozentuale Angabe ausgegeben wird.\n"
3735 "Deine aktuelle Einstellung ist %d%s Zeilen (%s).",
3736 QueryProp(P_SCREENSIZE),
3737 Query(P_SCREENSIZE) < 0 ? " 'automatische'" : "",
3738 QueryProp(P_MORE_FLAGS) & E_ABS ? "absolut" : "relativ"),78,0,1));
3739 return 1;
3740 }
3741
3742 SetProp( P_SCREENSIZE, size );
3743
3744 printf( "Okay, Deine Zeilenzahl steht nun auf %d.\n", size );
3745 return 1;
3746}
3747
3748static int _query_screensize()
3749{
3750 int sz,rows;
3751
3752 if ( (sz=Query(P_SCREENSIZE)) >= 0 )
3753 return sz;
3754
3755 if ( !rows=QueryProp(P_TTY_ROWS) )
3756 return 0;
3757
3758 return (rows+=sz) >= 5 ? rows : 5;
3759}
3760
3761static int presay(string str)
3762{
3763 if (!str=_unparsed_args())
3764 write("Dein Presay ist jetzt geloescht.\n");
3765 else
3766 printf("Dein Presay lautet jetzt: \"%s\".\n",str=capitalize(str));
3767 SetProp(P_PRESAY,str);
3768 return 1;
3769}
3770
3771static int sethands(string str)
3772{
3773 mixed *hands;
3774
3775 if (!(str=_unparsed_args()))
3776 {
3777 write("sethands <message>\n");
3778 return 1;
3779 }
3780 if (str=="0")
3781 hands=RaceDefault(P_HANDS);
3782 if (!hands || !pointerp(hands))
3783 hands=Query(P_HANDS);
3784 hands[0]=" "+str;
3785 Set(P_HANDS,hands);
3786 write("Ok.\n");
3787 return 1;
3788}
3789
3790static int inform(string str)
3791{
3792 switch (str) {
3793 case "on":
3794 case "ein":
3795 case "an":
3796 if (Query(P_INFORMME))
3797 write("Das hattest Du schon so eingestellt.\n");
3798 else
3799 {
3800 write("Kuenftig wirst Du informiert, wenn jemand das "MUDNAME" verlaesst/betritt.\n");
3801 Set(P_INFORMME,1);
3802 }
3803 return 1;
3804 case "aus":
3805 case "off":
3806 if (!Query(P_INFORMME))
3807 write("Das hattest Du schon so eingestellt.\n");
3808 else
3809 {
3810 write("Ok.\n");
3811 Set(P_INFORMME,0);
3812 }
3813 return 1;
3814 case 0:
3815 write("Inform-Mode ist "+(Query(P_INFORMME)?"an":"aus")+"geschaltet.\n");
3816 return 1;
3817 }
3818 write("inform an oder inform aus, bitte.\n");
3819 return 1;
3820}
3821
3822void delayed_write(mixed *what)
3823{
3824 if (!pointerp(what)||!sizeof(what)||!pointerp(what[0]))
3825 return;
3826 tell_object(ME,what[0][0]);
3827 if (sizeof(what)>1&&sizeof(what[0])>1)
3828 call_out("delayed_write",what[0][1],what[1..]);
3829}
3830
3831void notify_player_change(string who, int rein, int invis)
3832{
3833 string *list,name;
3834 mixed mlist;
3835
3836 if (invis) name="("+who+")";
3837 else name=who;
3838
3839 if (Query(P_INFORMME))
3840 {
3841 if (rein)
3842 tell_object(ME,name+" ist gerade ins "MUDNAME" gekommen.\n");
3843 else
3844 tell_object(ME,name+" hat gerade das "MUDNAME" verlassen.\n");
3845 }
3846
3847 if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
3848
3849 if(pointerp(list=Query(P_WAITFOR)) && sizeof(list) && member(list,who)!=-1)
3850 {
Bugfix3afcb792022-01-21 22:32:42 +01003851 if (!(QueryProp(P_ALERT) & AL_NO_SOUND))
MG Mud User88f12472016-06-24 23:31:02 +02003852 name+=sprintf("%c",7); // Char fuer Pieps an den String anhaengen.
3853 // Moechte der Spieler keine ASCII-Grafik sehen, wird diese Meldung ohne
3854 // Leerzeichen formatiert, so dass sie von Screenreadern vorgelesen wird.
3855 // Anderenfalls wuerde sie einzeln buchstabiert.
3856 if ( QueryProp(P_NO_ASCII_ART) )
3857 {
Bugfixe6503fb2025-02-21 18:44:47 +01003858 delayed_write( ({ ({ sprintf("%s IST JETZT %sDA!!!\n",
MG Mud User88f12472016-06-24 23:31:02 +02003859 name, (rein?"":"NICHT MEHR ")) }) }) );
3860 }
3861 else
3862 {
3863 delayed_write( ({ ({ sprintf("%s I S T J E T Z T %sD A !!!\n",
3864 name, (rein?"":"N I C H T M E H R ")) }) }) );
3865 }
3866 }
3867
3868 if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
3869 (mappingp(mlist)) && (mlist[who]))
3870 Show_WaitFor_Reason(who,invis);
3871}
3872
3873static int erwarte(string str)
3874{
3875 string *list,*str1;
3876 mixed mlist;
MG Mud User88f12472016-06-24 23:31:02 +02003877
3878 if (!mappingp(mlist=QueryProp(P_WAITFOR_REASON)))
3879 mlist=([]);
3880 if (!pointerp(list=Query(P_WAITFOR)))
3881 list=({});
3882
3883 if (!str || str=="-u")
3884 {
3885 if(Query(P_WAITFOR_FLAGS)&0x01)
3886 write("Du hast 'erwarte' temporaer deaktiviert.\n");
3887 write("Du erwartest jetzt");
3888 if (!sizeof(list))
3889 write(" niemanden mehr.\n");
3890 else
3891 {
3892 write(":\n");
3893 if (!str) list=sort_array(list,#'>);
3894 More(break_string(CountUp(list),78));
3895 }
3896 return 1;
3897 }
3898 if(str=="aus"){
3899 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)|0x01);
3900 write("Erwarte ist jetzt deaktiviert.\n");
3901 return 1;
3902 }
3903 if(str=="an" || str=="ein"){
3904 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)&0xFE);
3905 write("Erwarte ist jetzt aktiv.\n");
3906 return 1;
3907 }
3908
3909 str1=explode(_unparsed_args()||""," ");
3910 if (sizeof(str1)==1)
3911 {
3912 if (str1[0]!="wegen")
3913 {
3914 str=capitalize(lower_case(str));
3915 if (member(list,str)!=-1)
3916 {
3917 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,str));
3918 list-=({str});
3919 write(str+" aus der Liste entfernt.\n");
3920 } else
3921 {
3922 if (sizeof(list)>1000)
3923 {
3924 write("Du erwartest schon genuegend!\n");
3925 return 1;
3926 }
3927 list+=({str});
3928 write(str+" an die Liste angehaengt.\n");
3929 }
3930 Set(P_WAITFOR,list);
3931 }
3932 else
3933 {
Zesstraf253faa2018-07-27 13:05:13 +02003934 if (sizeof(mlist))
MG Mud User88f12472016-06-24 23:31:02 +02003935 {
3936 write("Du erwartest aus einem bestimmten Grund:\n");
Zesstraf253faa2018-07-27 13:05:13 +02003937 write(break_string(CountUp(sort_array(m_indices(mlist),
3938 #'>))+".",78));
MG Mud User88f12472016-06-24 23:31:02 +02003939 }
3940 else write("Du erwartest niemanden aus einem bestimmten Grund.\n");
3941 }
3942 return 1;
3943 }
3944 notify_fail("Falsche Syntax, siehe 'hilfe erwarte'!\n");
3945 if (str1[1]!="wegen") return 0;
3946 if (sizeof(str1)==2)
3947 Show_WaitFor_Reason(capitalize(lower_case(str1[0])),0);
3948 else {
3949 string s=capitalize(lower_case(str1[0]));
3950 if (sizeof(str1)==3 && (str1[2]=="nichts" || str1[2]=="loeschen"))
3951 if (!mlist[s])
3952 write("Du hast "+s+" aus keinem bestimmten Grund erwartet!\n");
3953 else
3954 {
3955 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,s));
3956 write("Du erwartest "+s+" aus keinem bestimmten Grund mehr!\n");
3957 }
3958 else
3959 {
Zesstra27649642018-07-27 12:59:25 +02003960 // Menge an erwarte-wegen Eintraegen begrenzen.
Zesstra5d9a0d72018-07-27 13:19:14 +02003961 int lim;
Zesstra27649642018-07-27 12:59:25 +02003962 if (IS_ARCH(ME)) lim=120;
3963 else if (IS_LEARNER(ME)) lim=80;
3964 else if (IS_SEER(ME)) lim=60;
3965 else lim=30;
3966 if (!mlist[s] && sizeof(mlist)>=lim)
MG Mud User88f12472016-06-24 23:31:02 +02003967 write("Sorry, aber Du erwartest schon genuegend Leute!\n");
3968 else
3969 {
Arathorn9b05bb42019-11-14 13:24:29 +01003970 // Meldung wieder zusammensetzen
3971 string meldung = implode(str1[2..], " ");
3972 // und Laenge auf 78 Zeichen abschneiden.
3973 meldung = sprintf("%.78s", meldung);
3974 m_add(mlist, s, meldung);
3975 SetProp(P_WAITFOR_REASON, mlist);
MG Mud User88f12472016-06-24 23:31:02 +02003976 Show_WaitFor_Reason(s,0);
3977 }
3978 }
3979 }
3980 return 1;
3981}
3982
3983static int uhrmeldung(string str)
3984{
3985 if (!(str=_unparsed_args()))
3986 {
3987 str=QueryProp(P_CLOCKMSG);
3988 if (!str)
3989 {
3990 write("Du hast die Standard-Uhrmeldung.\n");
3991 return 1;
3992 }
3993 if( !stringp(str) ) str = sprintf("%O\n",str);
3994 printf("Deine Uhrmeldung ist:\n%s\n",str[0..<2]);
3995 return 1;
3996 }
3997 if (str=="0")
3998 {
3999 SetProp(P_CLOCKMSG,0);
4000 write("Ok, Du hast jetzt wieder die Standard-Meldung.\n");
4001 return 1;
4002 }
4003 if (sizeof(explode(str,"%d"))>2)
4004 {
4005 write("Fehler, es darf nur ein %d in der Meldung vorkommen.\n");
4006 return 1;
4007 }
4008 /* Mehrere %-Parameter verursachen das Abschalten der Uhr zur vollen Stunde.
4009 */
4010 if (sizeof(explode(str,"%"))>2)
4011 {
4012 write("Fehler: Zuviele %-Parameter in der Meldung.\n");
4013 return 1;
4014 }
4015 /* Nur ein %-Parameter, aber der falsche: nicht sinnvoll. */
4016 else
4017 {
4018 int i = strstr(str,"%",0);
4019 if ( i>-1 && ( i==sizeof(str)-1 || str[i+1]!='d'))
4020 {
4021 write("Fehler: Falscher %-Parameter in der Meldung.\n");
4022 return 1;
4023 }
4024 }
4025 str+="\n";
4026 SetProp(P_CLOCKMSG,str);
4027 write("Ok.\n");
4028 return 1;
4029}
4030
4031static int zeitzone(string str)
4032{
4033 int zt;
4034 if(!str || str==""){
4035 if(!(zt=QueryProp(P_TIMEZONE)))
4036 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
4037 "eingestellt.\n");
4038 else if(zt>0)
4039 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
4040 "eingestellt.\n",zt);
4041 else
4042 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
4043 "Berlin eingestellt.\n",-zt);
4044 return 1;
4045 }
4046 if(sscanf(str,"utc %d",zt)==1) zt=(zt-1)%24;
4047 else zt=to_int(str)%24;
4048
4049 SetProp(P_TIMEZONE,zt);
4050
4051 if(!zt)
4052 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
4053 "eingestellt.\n");
4054 else if(zt>0)
4055 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
4056 "eingestellt.\n",zt);
4057 else
4058 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
4059 "Berlin eingestellt.\n",-zt);
4060 return 1;
4061}
4062
4063static int emailanzeige(string str){
4064 notify_fail("Syntax: emailanzeige [alle|freunde|niemand]\n");
4065 if(!str || str==""){
4066 if(!(str=QueryProp(P_SHOWEMAIL)))str="Niemandem";
4067 else if(str=="alle")str="allen";
4068 else if(str=="freunde")str="Deinen Freunden";
4069 else if(str=="niemand")str="niemandem";
4070 else{
4071 SetProp(P_SHOWEMAIL,0);
4072 str="Niemandem";
4073 }
4074 write("Deine Email wird "+str+" angezeigt.\n");
4075 return 1;
4076 }
4077 else if(member(({"alle","freunde","niemand"}),str)==-1)return 0;
4078
4079 SetProp(P_SHOWEMAIL,str);
4080
4081 if(str=="alle")str="allen";
4082 else if(str=="freunde")str="Deinen Freunden";
4083 else str="niemandem";
4084 write("Deine Email wird "+str+" angezeigt.\n");
4085 return 1;
4086}
4087
4088static int zaubertraenke()
4089{
4090 More("/room/orakel"->TipListe());
4091 return 1;
4092}
4093
4094varargs static int angriffsmeldung(string arg) {
4095 if (arg=="ein" || arg=="an")
4096 SetProp(P_SHOW_ATTACK_MSG,1);
4097 else if (arg=="aus")
4098 SetProp(P_SHOW_ATTACK_MSG,0);
4099 if (QueryProp(P_SHOW_ATTACK_MSG))
4100 write("Du siehst saemtliche Angriffsmeldungen von Dir.\n");
4101 else
4102 write("Du siehst nur neue Angriffsmeldungen von Dir.\n");
4103 return 1;
4104}
4105
4106static mixed _query_localcmds()
4107{
4108 return ({({"zeilen","set_screensize",0,0}),
4109 ({"email","set_email",0,0}),
4110 ({"url","set_homepage",0,0}),
4111 ({"icq","set_icq",0,0}),
4112 ({"messenger", "set_messenger", 0, 0}),
4113 ({"ort","set_location",0,0}),
4114 ({"punkte","short_score",0,0}),
4115 ({"score","short_score",0,0}),
4116 ({"info","score",0,0}),
4117 ({"kurzinfo","very_short_score",0,0}),
4118 ({"quit","new_quit",0,0}),
4119 ({"ende","new_quit",0,0}),
4120 ({"disconnect","disconnect",0,0}),
4121 ({"schlaf","disconnect",1,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004122 ({"toete","kill",0,0}),
4123 ({"angriffsmeldung","angriffsmeldung",0,0}),
4124 ({"passw","change_password",1,0}),
4125 ({"hilfe","help",1,0}),
4126 ({"selbstloeschung","self_delete",0,0}),
4127 ({"spielpause","spielpause",0,0}),
4128 ({"spieldauer","spieldauer",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004129 ({"idee","ReportError",0,0}),
4130 ({"typo","ReportError",0,0}),
4131 ({"bug","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004132 ({"fehler","fehlerhilfe",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004133 ({"md","ReportError",0,0}),
4134 ({"detail","ReportError",0,0}),
Bugfixa75344d2017-06-16 14:04:48 +02004135 ({"syntaxhinweis","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004136 ({"vorsicht","toggle_whimpy",0,0}),
4137 ({"stop","stop",0,0}),
4138 ({"kwho","kwho",0,0}),
4139 ({"kwer","kwho",0,0}),
4140 ({"kkwer","kkwho",0,0}),
4141 ({"kkwho","kkwho",0,0}),
4142 ({"who","who",0,0}),
4143 ({"wer","who",0,0}),
4144 ({"zeit","uhrzeit",0,0}),
4145 ({"uhrzeit","uhrzeit",0,0}),
4146 ({"weg","weg",0,0}),
4147 ({"wegmeldung", "wegmeldung", 0, 0}),
4148 ({"idlezeit", "idlezeit", 0, 0}),
4149 ({"finger","finger",0,0}),
4150 ({"muds","muds",0,0}),
4151 ({"emote","emote",0,0}),
4152 ({":","emote",1,0}),
4153 ({";","emote",1,0}),
4154 ({"remote","remote",0,SEER_LVL}),
4155 ({"r:","remote",1,0}),
4156 ({"r;","gremote",1,0}),
4157 ({"titel","set_title",0,0}),
4158 ({"review","review",0,SEER_LVL}),
4159 ({"setmin","setmin",0,SEER_LVL}),
4160 ({"setmout","setmout",0,SEER_LVL}),
4161 ({"setmmin","setmmin",0,SEER_LVL}),
4162 ({"setmmout","setmmout",0,SEER_LVL}),
4163 ({"sethands","sethands",0,SEER_LVL}),
4164 ({"presay","presay",0,SEER_LVL}),
4165 ({"extralook","extralook",0,SEER_LVL}),
4166 ({"fluchtrichtung","toggle_whimpy_dir",0,SEER_LVL}),
4167 ({"inform","inform",0,0}),
4168 ({"erwarte","erwarte",0,0}),
4169 ({"stty","stty",0,0}),
4170 ({"grafik", "set_ascii_art", 0, 0}),
4171 ({"uhrmeldung","uhrmeldung",0,0}),
4172 ({"zeitzone","zeitzone",0,0}),
4173 ({"behalte","behalte",0,0}),
4174 ({"zweitiemarkierung","zweitiemarkierung",0,0}),
4175 ({"emailanzeige","emailanzeige",0,0}),
4176 ({"topliste","topliste",0,0}),
4177 ({"ton","set_visualbell",0,0}),
4178 ({"telnegs","show_telnegs",0,0}),
4179 ({"spotte", "spotte", 0, 0}),
4180 ({"reise","reise",0,0}),
4181 ({"zaubertraenke","zaubertraenke",0,0}),
4182 ({"telnet","telnet_cmd",0,0}),
4183 })+
4184 command::_query_localcmds()+
4185 viewcmd::_query_localcmds()+
4186 comm::_query_localcmds()+
4187 skills::_query_localcmds()+
4188 description::_query_localcmds();
4189}
4190
4191static int _check_keep(object ob)
4192{
4193 return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
4194}
4195
4196static mixed _set_testplayer(mixed arg) {
4197 mixed res;
4198 object setob;
4199
4200 setob=this_player();
4201 if (!objectp(setob) || !query_once_interactive(setob))
4202 setob=this_interactive();
4203 if (!objectp(setob))
4204 setob=previous_object();
4205 if (setob && !IS_DEPUTY(setob)) {
4206 arg=geteuid(setob);
4207 if (!arg || arg=="NOBODY")
4208 arg=getuid(setob);
4209 arg=capitalize(arg);
4210 }
4211 res=Set(P_TESTPLAYER,arg);
4212 Set(P_TESTPLAYER,PROTECTED,F_MODE_AS);
4213 return res;
4214}
4215
4216int zweitiemarkierung(string arg)
4217{
4218 if (!QueryProp(P_SECOND))
4219 return _notify_fail("Aber Du bist doch gar kein Zweiti.\n"),0;
4220 notify_fail("Syntax: zweitiemarkierung [unsichtbar|sichtbar|name]\n");
4221 if (!arg)
4222 return 0;
4223 switch (arg)
4224 {
4225 case "unsichtbar" :
4226 SetProp(P_SECOND_MARK,-1);
4227 write("Jetzt sieht kein Spieler mehr, dass Du ein Zweiti bist.\n");
4228 return 1;
4229 case "sichtbar" :
4230 SetProp(P_SECOND_MARK,0);
4231 write("Jetzt sieht kein Spieler mehr, wessen Zweiti Du bist.\n");
4232 return 1;
4233 case "name" :
4234 SetProp(P_SECOND_MARK,1);
4235 write("Jetzt koennen alle sehen, wessen Zweiti Du bist.\n");
4236 return 1;
4237 }
4238 return 0;
4239}
4240
4241int topliste(string arg)
4242{
4243 if (!arg)
4244 {
4245 printf("Du hast Dich fuer die Topliste %s.\n",
4246 (QueryProp(P_NO_TOPLIST) ? "gesperrt" : "freigegeben"));
4247 return 1;
4248 }
4249 else if (member(({"j","ja","n","nein"}),arg)==-1)
4250 return _notify_fail("Syntax: topliste [ja|nein]\n"),0;
4251 if (arg[0]=='j')
4252 {
4253 SetProp(P_NO_TOPLIST,0);
4254 write("Du kannst jetzt (theoretisch) in der Topliste auftauchen.\n");
4255 }
4256 else
4257 {
4258 SetProp(P_NO_TOPLIST,1);
Zesstradd2d1982017-01-28 14:03:19 +01004259 "/secure/topliste"->DeletePlayer();
4260 write("Du wirst jetzt nicht (mehr) in den Toplisten auftauchen.\n");
MG Mud User88f12472016-06-24 23:31:02 +02004261 }
4262 Set(P_NO_TOPLIST,SAVE|PROTECTED,F_MODE_AS);
4263 return 1;
4264}
4265
4266int show_telnegs(string arg)
4267{
4268 if (!arg)
4269 {
4270 write("Du bekommst Aenderungen Deiner Fenstergroesse "+
4271 (QueryProp(P_TTY_SHOW)?"":"nicht ")+"angezeigt.\n");
4272 return 1;
4273 }
4274 if (member(({"ein","an","aus"}),arg)==-1)
4275 {
4276 write("Syntax: telnegs [ein|aus]\n");
4277 return 1;
4278 }
4279 if (arg=="ein" || arg=="an")
4280 {
4281 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"":"nun ")+
4282 "Aenderungen Deiner Fenstergroesse angezeigt.\n");
4283 Set(P_TTY_SHOW,1);
4284 return 1;
4285 }
4286 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"nun ":"")+
4287 "Aenderungen Deiner Fenstergroesse nicht "+
4288 (QueryProp(P_TTY_SHOW)?"mehr ":"")+"angezeigt.\n");
4289 Set(P_TTY_SHOW,0);
4290 return 1;
4291}
4292
4293private int set_keep_alive(string str) {
4294 if (str == "ein") {
Zesstra268e3fd2019-07-25 14:29:01 +02004295 telnet_tm_counter = QueryProp(P_TELNET_KEEPALIVE_DELAY) || (240 / __HEART_BEAT_INTERVAL__);
4296 tell_object(this_object(), break_string( sprintf(
4297 "An Deinen Client werden jetzt alle %i Sekunden unsichtbare Daten "
MG Mud User88f12472016-06-24 23:31:02 +02004298 "geschickt, um zu verhindern, dass Deine Verbindung zum "MUDNAME
Zesstra268e3fd2019-07-25 14:29:01 +02004299 " beendet wird.",
4300 telnet_tm_counter*__HEART_BEAT_INTERVAL__), 78));
4301 // Bei Magiern ist der HB evtl. ausgeschaltet und muss eingeschaltet
4302 // werden.
4303 if (!object_info(this_object(), OC_HEART_BEAT))
4304 configure_object(this_object(), OC_HEART_BEAT, 1);
MG Mud User88f12472016-06-24 23:31:02 +02004305 }
4306 else if (str == "aus") {
4307 telnet_tm_counter = 0;
4308 tell_object(this_object(),break_string(
4309 "Du hast das Senden von unsichtbaren Daten (Keep-Alive-Pakete) an "
4310 "Deinen Client ausgeschaltet.",78));
4311 }
4312 else {
4313 if (!telnet_tm_counter)
4314 tell_object(this_object(), break_string(
4315 "An Deinen Client werden keine Keep-Alive-Pakete geschickt.",78));
4316 else
Bugfix5e832512022-10-26 17:13:07 +02004317 {
MG Mud User88f12472016-06-24 23:31:02 +02004318 tell_object(this_object(), break_string(
Bugfix5e832512022-10-26 17:13:07 +02004319 "An Deinen Client werden alle "
4320 + QueryProp(P_TELNET_KEEPALIVE_DELAY) * __HEART_BEAT_INTERVAL__
4321 + " Sekunden unsichtbare Daten geschickt, damit Deine Verbindung "
MG Mud User88f12472016-06-24 23:31:02 +02004322 "zum "MUDNAME" nicht beendet wird.",78));
Bugfix5e832512022-10-26 17:13:07 +02004323 }
MG Mud User88f12472016-06-24 23:31:02 +02004324 }
4325 return 1;
4326}
4327
4328private int print_telnet_rttime() {
4329 int rtt = QueryProp(P_TELNET_RTTIME);
4330 if (rtt>0)
4331 tell_object(ME, break_string(
4332 "Die letzte gemessene 'round-trip' Zeit vom MG zu Deinem Client "
4333 "und zurueck betrug " + rtt + " us.",78));
4334 else
4335 tell_object(ME, break_string(
4336 "Bislang wurde die 'round-trip' Zeit vom MG zu Deinem Client "
4337 "noch nicht gemessen oder Dein Client unterstuetzt dieses "
4338 "nicht.",78));
4339 return 1;
4340}
4341
Zesstra57cdbc32020-01-20 23:17:10 +01004342// Falls es eine per telnet vom Client ausgehandelte Einstellung fuer CHARSET
4343// gibt, hat die manuelle Einstellung von Spielern hier geringere Prioritaet
4344// und bildet nur den Fallback.
Zesstra9ab40222020-01-16 23:07:12 +01004345private int set_telnet_charset(string enc) {
Zesstra57cdbc32020-01-20 23:17:10 +01004346 struct telopt_s tdata = query_telnet_neg()[TELOPT_CHARSET];
Zesstra9ab40222020-01-16 23:07:12 +01004347 if (!sizeof(enc))
4348 {
Zesstra57cdbc32020-01-20 23:17:10 +01004349 if (!tdata->data || !tdata->data["accepted_charset"])
4350 {
4351 tell_object(ME, break_string(sprintf(
4352 "Zur Zeit ist der Zeichensatz \'%s\' aktiv. "
4353 "Alle Ausgaben an Dich werden in diesem Zeichensatz gesendet "
4354 "und wir erwarten alle Eingaben von Dir in diesem Zeichensatz. ",
4355 interactive_info(ME, IC_ENCODING)), 78));
4356 }
4357 else
4358 {
4359 tell_object(ME, break_string(sprintf(
Zesstra9ab40222020-01-16 23:07:12 +01004360 "Zur Zeit ist der Zeichensatz \'%s\' aktiv. "
4361 "Alle Ausgaben an Dich werden in diesem Zeichensatz gesendet "
4362 "und wir erwarten alle Eingaben von Dir in diesem Zeichensatz. "
Zesstra57cdbc32020-01-20 23:17:10 +01004363 "Dieser Zeichensatz wurde von Deinem Client ausgehandelt.",
4364 interactive_info(ME, IC_ENCODING)), 78));
4365 if (QueryProp(P_TELNET_CHARSET))
4366 tell_object(ME, break_string(sprintf(
4367 "Dein manuell eingestellter Zeichensatz ist \'%s\', welcher "
4368 "aber nur genutzt wird, wenn Dein Client keinen Zeichensatz "
4369 "aushandelt.", QueryProp(P_TELNET_CHARSET)),78));
4370
4371 }
Zesstra9ab40222020-01-16 23:07:12 +01004372 }
Zesstra57cdbc32020-01-20 23:17:10 +01004373 // Wenn es "loeschen" ist, wird die Prop genullt und wir stellen den Default
4374 // ein. Allerdings nur, wenn nix per telnet ausgehandelt wurde, dann wird
4375 // das beibehalten.
Zesstra9ab40222020-01-16 23:07:12 +01004376 else if (lower_case(enc) == "loeschen")
4377 {
4378 SetProp(P_TELNET_CHARSET, 0);
Zesstra57cdbc32020-01-20 23:17:10 +01004379 // wurde was per telnet option charset ausgehandelt? dann wird (weiterhin)
4380 // das genommen und nicht umgestellt.
4381 if (!tdata->data || !tdata->data["accepted_charset"])
4382 {
4383 configure_interactive(ME, IC_ENCODING, interactive_info(0,IC_ENCODING));
4384 tell_object(ME, break_string(sprintf(
Zesstra9ab40222020-01-16 23:07:12 +01004385 "Der Default \'%s\' wurde wieder hergestellt. "
4386 "Alle Ausgaben an Dich werden in diesem Zeichensatz gesendet "
4387 "und wir erwarten alle Eingaben von Dir in diesem Zeichensatz. "
4388 "Sollte Dein Client die Telnet-Option CHARSET unterstuetzen, kann "
Zesstra57cdbc32020-01-20 23:17:10 +01004389 "dieser allerdings direkt einen Zeichensatz aushandeln oder "
4390 "ausgehandelt haben, der dann stattdessen gilt.",
Zesstra9ab40222020-01-16 23:07:12 +01004391 interactive_info(ME, IC_ENCODING)), 78));
Zesstra57cdbc32020-01-20 23:17:10 +01004392 }
4393 else
4394 {
4395 tell_object(ME, break_string(sprintf(
4396 "Der Default \'%s\' wurde wieder hergestellt. Allerdings hat "
4397 "Dein Client mit dem MG den Zeichensatz \'%s\' ausgehandelt, "
4398 "welcher immer noch aktiv ist.",
4399 interactive_info(0, IC_ENCODING),
4400 interactive_info(ME, IC_ENCODING)), 78));
4401 }
Zesstra9ab40222020-01-16 23:07:12 +01004402 }
4403 else
4404 {
Zesstra57cdbc32020-01-20 23:17:10 +01004405 // Wenn der Zeichensatz keine //-Variante ist, machen wir den zu
Zesstra9ab40222020-01-16 23:07:12 +01004406 // einer. Das verhindert letztlich eine Menge Laufzeitfehler, wenn ein
4407 // Zeichen mal nicht darstellbar ist.
Zesstra57cdbc32020-01-20 23:17:10 +01004408 if (strstr(enc, "//") == -1)
Zesstra9ab40222020-01-16 23:07:12 +01004409 enc += "//TRANSLIT";
4410 if (catch(configure_interactive(ME, IC_ENCODING, enc); nolog))
4411 {
4412 tell_object(ME, break_string(sprintf(
4413 "Der Zeichensatz \'%s\' ist nicht gueltig oder zumindest auf "
4414 "diesem System nicht verwendbar.", enc),78));
4415 }
4416 else
4417 {
4418 SetProp(P_TELNET_CHARSET, interactive_info(ME, IC_ENCODING));
Zesstra57cdbc32020-01-20 23:17:10 +01004419 if (!tdata->data || !tdata->data["accepted_charset"])
4420 {
4421 tell_object(ME, break_string(sprintf(
Zesstra9ab40222020-01-16 23:07:12 +01004422 "Der Zeichensatz \'%s\' wurde eingestellt. Alle Ausgaben an "
4423 "Dich werden in diesem Zeichensatz gesendet und wir erwarten "
4424 "alle Eingaben von Dir in diesem Zeichensatz. Sollte Dein "
4425 "Client die Telnet-Option CHARSET unterstuetzen, kann "
4426 "dieser allerdings direkt einen Zeichensatz aushandeln, der "
4427 "dann stattdessen gilt.",
4428 interactive_info(ME, IC_ENCODING)),78));
Zesstra57cdbc32020-01-20 23:17:10 +01004429 }
4430 else
4431 {
4432 // Der via telnet ausgehandelte Charset muss wieder hergestellt
4433 // werden.
4434 configure_interactive(ME, IC_ENCODING,
4435 tdata->data["accepted_charset"]);
4436 tell_object(ME, break_string(sprintf(
4437 "Der Zeichensatz \'%s\' wurde gespeichert. Allerdings hat "
4438 "Dein Client mit dem MG den Zeichensatz \'%s\' ausgehandelt, "
4439 "welcher immer noch aktiv ist.",
4440 QueryProp(P_TELNET_CHARSET),
4441 interactive_info(ME, IC_ENCODING)), 78));
4442 }
Zesstra9ab40222020-01-16 23:07:12 +01004443 }
Zesstra57cdbc32020-01-20 23:17:10 +01004444
Zesstra9ab40222020-01-16 23:07:12 +01004445 }
4446 return 1;
4447}
4448
MG Mud User88f12472016-06-24 23:31:02 +02004449int telnet_cmd(string str) {
4450 if (!str) return 0;
4451 string *args = explode(str, " ");
4452 string newargs;
4453 if (sizeof(args) > 1)
4454 newargs = implode(args[1..], " ");
4455 else
4456 newargs = "";
4457
4458 switch(args[0])
4459 {
4460 case "keepalive":
4461 return set_keep_alive(newargs);
4462 case "rttime":
4463 return print_telnet_rttime();
Zesstra9ab40222020-01-16 23:07:12 +01004464 case "charset":
4465 return set_telnet_charset(newargs);
Zesstraab567652019-01-15 00:20:05 +01004466 case "tls":
Zesstra035bc7b2021-07-06 22:24:32 +02004467#if __EFUN_DEFINED__(tls_query_connection_state)
Zesstra363b1382019-01-15 00:36:24 +01004468 if (tls_query_connection_state(ME) > 0)
Zesstraab567652019-01-15 00:20:05 +01004469 tell_object(ME,
4470 "Deine Verbindung zum Morgengrauen ist TLS-verschluesselt.\n");
4471 else
Zesstra035bc7b2021-07-06 22:24:32 +02004472#endif
Zesstraab567652019-01-15 00:20:05 +01004473 tell_object(ME,
4474 "Deine Verbindung zum Morgengrauen ist nicht verschluesselt.\n");
4475 return 1;
Zesstrac7723982021-06-10 23:13:16 +02004476 case "client-gui":
Zesstra57e78832022-11-11 22:55:04 +01004477 case "gui":
Zesstrac7723982021-06-10 23:13:16 +02004478 GMCP_offer_clientgui(newargs);
4479 return 1;
MG Mud User88f12472016-06-24 23:31:02 +02004480 }
4481 return 0;
4482}
4483
4484int spotte( string str )
4485{
4486 _notify_fail( "Hier ist nichts, was Du verspotten koenntest!\n" );
4487 return 0;
4488}
4489
4490int behalte(string str)
4491{
4492 object ob,*obs;
4493 string s;
4494
4495 if (str)
4496 {
4497 if (str=="alles") {
4498 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, getuid());
4499 write("Ok!\n");
4500 return 1;
4501 }
4502 if (str=="nichts") {
4503 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, 0);
4504 write("Ok!\n");
4505 return 1;
4506 }
Arathorndfbf8d22021-03-25 21:17:10 +01004507 if (!sizeof(obs=find_obs(str,PUT_GET_DROP)))
MG Mud User88f12472016-06-24 23:31:02 +02004508 {
4509 _notify_fail("Aber sowas hast Du nicht dabei!\n");
4510 return 0;
4511 }
4512 else ob=obs[0];
4513
4514 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4515 ob->SetProp(P_KEEP_ON_SELL,0);
4516 else
4517 ob->SetProp(P_KEEP_ON_SELL,geteuid(ME));
4518
4519 // erneut abfragen, da sich der Wert nicht geaendert haben muss
4520 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4521 write(break_string(sprintf("Ok, Du wirst %s jetzt bei 'verkaufe alles' "
4522 "behalten.\n",ob->name(WEN)),78));
4523 else
4524 write(break_string(sprintf("Ok, Du wirst %s beim naechsten 'verkaufe "
4525 "alles' mitverkaufen!\n",ob->name(WEN)),78));
4526
4527 return 1;
4528 }
4529 s=make_invlist(ME,filter(all_inventory(ME),#'_check_keep)); //'));
4530 More(s);
4531 return 1;
4532}
4533
4534static int _query_lep()
4535{
4536 int val;
4537 val = LEPMASTER->QueryLEP();
4538 Set( P_LEP, val );
4539 return val;
4540}
4541
4542static mixed _set_fraternitasdonoarchmagorum(mixed arg)
4543{
4544 if (!intp(arg)) return -1;
4545
4546 if ((!previous_object(1)||object_name(previous_object(1))!=FAO_MASTER) &&
4547 (!this_interactive() || !IS_ARCH(this_interactive())))
4548 return -1;
4549
4550 if (!intp(arg)) return -1;
4551
4552 log_file("fao/P_FAO",sprintf("%s - %s P_FAO gesetzt auf %O\n",
4553 dtime(time()),query_real_name(),arg) );
4554 return Set(P_FAO,arg);
4555}
4556
Zesstraca502032020-02-05 19:56:09 +01004557nomask public string set_realip(string str)
MG Mud User88f12472016-06-24 23:31:02 +02004558{
Zesstraca502032020-02-05 19:56:09 +01004559 if(previous_object()
4560 && strstr(object_name(previous_object()),"/secure")==0)
MG Mud User88f12472016-06-24 23:31:02 +02004561 {
4562 realip=str;
4563 }
Zesstraca502032020-02-05 19:56:09 +01004564 return realip;
MG Mud User88f12472016-06-24 23:31:02 +02004565}
4566
Zesstraca502032020-02-05 19:56:09 +01004567nomask public string query_realip()
MG Mud User88f12472016-06-24 23:31:02 +02004568{
Zesstraca502032020-02-05 19:56:09 +01004569 return realip ? realip : 0;
MG Mud User88f12472016-06-24 23:31:02 +02004570}
4571
4572mixed _query_netdead_env() {
4573 return ndead_location || ndead_l_filename;
4574}