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