blob: 347a695de63d32ed363394ff2dbd7847f8fcb361 [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.
540 if ( !(flags & CNP_FLAG_SILENT) && environment()
541 && object_name(environment()) != NETDEAD_ROOM )
542 {
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
Zesstra9ea7e9a2021-11-01 11:33:34 +0100581 // und wenn nicht silent: dann noch die anderen Spieler informieren
582 if (!(flags & CNP_FLAG_SILENT))
583 {
584 string wer = getuid(ME);
585 int mag = IS_LEARNER(ME);
586 int invis = QueryProp(P_INVIS);
587 object *u = users() - ({ME}); // sich selber nicht melden
588 if (mag && invis) { // Invismagier nur Magiern melden
589 u = filter(u, function int (object o)
590 { return query_wiz_level(o) >= LEARNER_LVL; }
591 );
592 }
593 u->notify_player_change(capitalize(wer),rein,invis);
MG Mud User88f12472016-06-24 23:31:02 +0200594 }
MG Mud User88f12472016-06-24 23:31:02 +0200595}
596
597/** Ruft im uebergebenen Objekt ein init() auf, sofern notwendig.
598 Ruft in ob ein init() auf, falls das Objekt nach dem
599 letzten Ausloggen geschaffen wurde.
600 \param[in] ob object - Objekt, in dem init() gerufen wird.
601 \param[in] logout int - Letzter Logout
602 \return 1, falls init() gerufen wurde. 0 sonst.
603*/
604static int call_init( object ob, int logout )
605{
606 if ( objectp(ob) && object_time(ob) > logout )
607 return catch(ob->init(); publish), 1;
608 return(0);
609}
610
611/** Holt seit dem letzten Ausloggen ausgefallene Inits nach.
612 Ruft in den uebergebenen Objekten call_init(), was einen init()
613 ausloest, falls das Objekt seit dem letzten Ausloggen erstellt wurde.
614 \param[in] logout Zeitpunkt des letzten Logouts
615 \param[in] obs Array von Objekten
616 \sa call_init()
617*/
618static void inits_nachholen( int logout, object *obs )
619{
620 filter( obs, "call_init", ME, logout );
621}
622
Zesstra9ab40222020-01-16 23:07:12 +0100623// Zeichensatz konfigurieren.
624// Wenn es einen manuell konfigurierten Zeichensatz gibt (der jetzt ja
625// eingelesen ist), wird der erstmal eingestellt.
626private void set_manual_encoding()
627{
628 string enc = QueryProp(P_TELNET_CHARSET);
629 if (enc)
630 {
631 if (stringp(enc) &&
632 !catch(configure_interactive(ME, IC_ENCODING, enc); publish))
633 {
634 // hat geklappt fertig
635 return;
636 }
637 // wenn kein string oder nicht erfolgreich -> Prop zuruecksetzen
638 tell_object(ME, sprintf(
639 "Der von Dir eingestellte Zeichensatz \'%s\' konnte nicht "
640 "eingestellt werden. Die Voreinstellung \'%s\' wurde wieder "
641 "eingestellt.", enc,
642 interactive_info(ME, IC_ENCODING)));
643 SetProp(P_TELNET_CHARSET, 0);
644 }
645}
646
MG Mud User88f12472016-06-24 23:31:02 +0200647/** Belebt einen Netztoten wieder.
648 Wird im Login gerufen, wenn der Spieler netztot war. Aequivalent zu
649 start_player()
Zesstra9ea7e9a2021-11-01 11:33:34 +0100650 @param[in] silent Wenn Flag gesetzt, werden div. Callbacks nicht gerufen und
651 Meldungen nicht ausgegeben. Wird vom Loginobjekt gesetzt, wenn das
652 Spielerobjekt bereits interaktiv ist und nur die Netzverbindung durch
653 Reconnect wechselt. *Nicht* zu verwechseln mit dem 'silent' von move & Co
654 oder durch P_INVIS!
MG Mud User88f12472016-06-24 23:31:02 +0200655 @param[in] ip Textuelle Repraesentation der IP-Adresse, von der der Spieler
Zesstra9ea7e9a2021-11-01 11:33:34 +0100656 kommt.
MG Mud User88f12472016-06-24 23:31:02 +0200657 @see start_player()
658*/
659varargs void Reconnect( int silent )
660{
661 int num;
662 string called_from_ip;
MG Mud User88f12472016-06-24 23:31:02 +0200663
664 if ( query_once_interactive(ME) )
665 {
Zesstra9ab40222020-01-16 23:07:12 +0100666 // Erstmal - sofern vorhanden - den manuell konfigurierten Zeichensatz
667 // einstellen. Im folgenden wird dann versucht, die TELOP CHARSET
668 // auszuhandeln, die aendert das evtl. nochmal.
669 set_manual_encoding();
MG Mud User88f12472016-06-24 23:31:02 +0200670 // perform the telnet negotiations. (all that are available)
671 "*"::startup_telnet_negs();
672 Set( P_LAST_LOGIN, time() );
673 }
674
675 enable_commands();
676 set_living_name( getuid() );
677 _remove_netdead();
678 set_heart_beat(1);
679 // Hunttimes aktualisieren und ggf. Feinde vergessen.
680 update_hunt_times((time()-QueryProp(P_LAST_LOGOUT)) /__HEART_BEAT_INTERVAL__);
681 // Heartbeats in Objekten im Inv reaktiveren.
682 restart_heart_beats();
Zesstra0681c732020-10-31 19:45:47 +0100683 // einige geerbte Module wollen ggf. was aufraeumen, neu initialisieren...
684 "*"::reconnect();
MG Mud User88f12472016-06-24 23:31:02 +0200685
Zesstrae88826c2016-09-24 20:37:05 +0200686 log_file( "syslog/shell/REENTER", sprintf( "%-11s %s, %-15s (%s).\n",
MG Mud User88f12472016-06-24 23:31:02 +0200687 capitalize(getuid(ME)), ctime(time())[4..15],
688 query_ip_number(ME)||"Unknown",
689 query_ip_name(ME)||"Unknown" ),
690 200000 );
691
692 if ( ndead_currently )
693 ndead_revive();
694
Zesstra9ea7e9a2021-11-01 11:33:34 +0100695 if ( interactive(ME) )
696 {
697 call_notify_player_change(
698 CNP_FLAG_ENTER | (silent ? CNP_FLAG_SILENT : 0 ) );
699 }
MG Mud User88f12472016-06-24 23:31:02 +0200700
MG Mud User88f12472016-06-24 23:31:02 +0200701 if ( query_once_interactive(ME) )
702 modify_prompt();
703
MG Mud User88f12472016-06-24 23:31:02 +0200704 catch( num = "secure/mailer"->FingerMail(geteuid());publish );
705
706 if ( num )
707 write( "Du hast " + num + " neue" + (num == 1 ? "n Brief" : " Briefe")
708 + " im Postamt liegen.\n" );
709
710 if ( QueryProp(P_AWAY) )
711 write( break_string( "Du bist als abwesend gekennzeichnet: " +
712 QueryProp(P_AWAY) + ".", 78 ) );
713
714 catch( RegisterChannels(); publish );
715
716 if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
717 query_ip_number(ME) != called_from_ip ) {
718 string tmp;
719
720 if ( stringp(tmp = query_ip_name(called_from_ip)) &&
721 tmp != called_from_ip )
722 tmp = " [" + tmp + "]";
723 else
724 tmp = "";
725
726 write( "Das letzte Mal kamst Du von " + called_from_ip + tmp + ".\n" );
727 }
728
729 Set( P_CALLED_FROM_IP, query_ip_number(ME) );
730
MG Mud User88f12472016-06-24 23:31:02 +0200731
732 // noch nicht geclonte Autoloader "nach"clonen
733 while ( remove_call_out("load_auto_objects") != -1 )
734 /* do nothing */;
735
736 if ( sizeof(autoload_rest) )
737 call_out( "load_auto_objects", 0, autoload_rest );
738
MG Mud User88f12472016-06-24 23:31:02 +0200739 NewbieIntroMsg();
740
741 if ( query_once_interactive(ME) )
742 ListAwaited();
743}
744
745/** Loggt einen Spieler aus und macht ihn netztot.
746 Bewegt einen Spieler in den Netztotenraum, deaktiviert Heartbeats im
747 Inventar, ruft BecomesNetDead(), loest Erwartemeldungen aus, triggert
748 Ausloggevent.
749*/
750void NetDead()
751{
MG Mud User88f12472016-06-24 23:31:02 +0200752 catch(RemoveChannels();publish);
753
754 if(query_hc_play()>1)
755 say("Ploetzlich weicht alle spirituelle Energie aus "+QueryProp(P_NAME)+".\n");
756 else
757 say("Ploetzlich weicht alles Leben aus "+QueryProp(P_NAME)+".\n");
758
759 _set_netdead();
760 remove_call_out("quit");
761 remove_living_name();
762 // Wird zwar im save_me() gemacht, aber die Zeitunterschiede beim
763 // fingern direkt nach dem Ausloggen fuehren immer wieder zu Verwirrungen
764 if(query_once_interactive(ME) && !QueryProp(P_INVIS))
765 Set(P_LAST_LOGOUT,time());
766 if (ME)
767 ndead_location = environment();
768
769 if (query_once_interactive(ME))
Zesstra9ea7e9a2021-11-01 11:33:34 +0100770 call_notify_player_change(CNP_FLAG_SLEEP);
MG Mud User88f12472016-06-24 23:31:02 +0200771
772 set_next_reset(900);
773 /* Bei Nicht-Magier-Shells wird comm::reset() aufgerufen, das prueft, ob
774 der Spieler immer noch netztot ist, und falls ja, die tmhist loescht.
775 Die Methode wird von /std/shells/magier.c ueberschrieben, netztote
776 Magier (die eigentlich schon anderweitig beseitigt worden sein sollten)
777 werden remove()d und destruct()ed. --Amynthor 05.05.2008 */
MG Mud User88f12472016-06-24 23:31:02 +0200778}
779
780
781/** Sendet ggf. Telnet Timing Marks als Keep-Alive Pakete an den Client.
782 * Wird in heart_beat() gerufen.
783 * @return 1, falls der Spieler Keep-Alive Paket wuenscht, sonst 0.
784 * @see heart_beat()
785*/
Zesstra268e3fd2019-07-25 14:29:01 +0200786protected int CheckTelnetKeepAlive(int delay) {
MG Mud User88f12472016-06-24 23:31:02 +0200787 if (telnet_tm_counter > 0) {
788 // Spieler hat offenbar ein Keep-Alive konfiguriert ...
789 if (!(--telnet_tm_counter)) {
790 // und das Intervall ist gerade abgelaufen.
791 // Prop offenbar gesetzt, FEature ist wirklich gewuenscht,
792 // Telnet Timing Mark senden
793 send_telnet_timing_mark();
794 // alle 120 HBs (240s, 4min).
795 // sollte eigentlich 240 / __HEART_BEAT_INTERVAL__ sein. Aber spart
796 // eine Operation im HB. ;-)
Zesstra268e3fd2019-07-25 14:29:01 +0200797 telnet_tm_counter = delay || 120;
MG Mud User88f12472016-06-24 23:31:02 +0200798 }
799 return 1; // Keep-Alive ist eingeschaltet
800 }
801 return 0;
802}
803
804static void ndead_move_me();
805
806/** Heartbeat des Spielerobjektes.
807 Prueft taegliche Spielzeit, speichert regelmaessig den Spieler,
808 bewegt Netztote in den Netztodenraum, ruft die HBs aus living/combat und
809 player/life.
810*/
811protected void heart_beat() {
812 if (!ME)
813 return;
814 if (ndead_currently)
815 {
816 if (interactive(ME))
817 {
818 ndead_revive();
819 ndead_location=0;
820 }
821 else return;
822 }
823 else
824 if (!(ndead_next_check--))
825 {
826 ndead_next_check=NETDEAD_CHECK_TIME;
827 if (!interactive(ME))
828 if (ndead_lasttime)
829 {
830 save_me(1);
831 if (IS_LEARNER(ME))
832 {
833 quit();
834 if (ME)
835 remove();
836 if (ME)
837 destruct(ME);
838 return;
839 }
840 ndead_move_me();
841 // Zumindest bei Gaesten ist das Objekt jetzt zerstoert
842 if (!objectp(this_object()))
843 return;
844 }
845 else
846 ndead_lasttime=1;
847 }
848 if (ME && ndead_lasttime && interactive(ME))
849 ndead_lasttime=0;
850 if (CheckDailyPlaytime())
851 return;
852
Zesstra268e3fd2019-07-25 14:29:01 +0200853 CheckTelnetKeepAlive(QueryProp(P_TELNET_KEEPALIVE_DELAY));
MG Mud User88f12472016-06-24 23:31:02 +0200854
855 life::heart_beat();
856 combat::heart_beat();
857 skills::heart_beat();
858}
859
860/** ID-Funktion fuer Spielerobjekte.
861 * id() fuer Spieler. Besondere Behandlung fuer Froesche und Geister,
862 * sowie Invis-Status.
863 * Gibt immer 0 zurueck, wenn P_INVIS && lvl < P_LEVEL
864 * @param[in] str angefragter ID-String
865 * @param[in] lvl Level des Anfragenden
866 * @return 1, falls str auf den Spieler zutrifft, 0 sonst.
867 */
868varargs int id(string str, int lvl)
869{
870 if (::id(str))
871 return 1;
872 if (Query(P_INVIS) && lvl < QueryProp(P_LEVEL))
873 return 0;
874 if (QueryProp(P_GHOST)&& str == "geist von "+ lower_case(QueryProp(P_NAME)))
875 return 1;
876 if (QueryProp(P_FROG) && str == "frosch") return 1;
877 return(0);
878}
879
880/** Setzt Spielerhomepage (Spielerkommando).
881 * @param[in] str Spielereingabe
882 * @return 1 bei Erfolg, 0 sonst.
883 */
884static int set_homepage(string str)
885{
886 mixed tmp;
887 if (!(str=_unparsed_args())) {
888 if (!QueryProp(P_HOMEPAGE))
889 write("Du hast keine URL-Adresse gesetzt!\n");
890 else
891 write("Deine offizielle URL-Adresse lautet: " + QueryProp(P_HOMEPAGE)
892 +"\n");
893 return 1;
894 }
895 write("Deine offizielle URL-Adresse wurde geaendert.\n");
896 if (str=="keine")
897 SetProp(P_HOMEPAGE, 0);
898 else {
Arathorndc28afc2018-11-26 22:20:59 +0100899 tmp = filter(regexplode(str, "[<][^>]*[>]"), function int (string e) {
900 return (e[0] != '<');
901 });
MG Mud User88f12472016-06-24 23:31:02 +0200902 write("Sie lautet jetzt: "+(str = implode(tmp, ""))+"\n");
903 SetProp(P_HOMEPAGE, str);
904 }
905 return 1;
906}
907
908/** Setzt Spieler-Wohnort (Spielerkommando).
909 * \param[in] str Spielereingabe
910 * \return 1 bei Erfolg, 0 sonst.
911 */
912static int set_location( string str )
913{
MG Mud User88f12472016-06-24 23:31:02 +0200914 if ( str == "0" || str == "loeschen" ){
915 Set( P_LOCATION, 0 );
916 write( "Du loescht Deine Ortsangabe.\n" );
917 return 1;
918 }
919
920 if ( stringp(str = _unparsed_args()) && str != "" ){
921 Set( P_LOCATION, capitalize(str) );
922 printf( "Du aenderst Deine Ortsangabe auf \"%s\".\n",
923 Query(P_LOCATION) );
924 }
Arathorn5513dfc2021-03-16 21:52:20 +0100925 else if ( stringp(Query(P_LOCATION)) )
MG Mud User88f12472016-06-24 23:31:02 +0200926 printf( "Deine Ortsangabe lautet \"%s\".\n", Query(P_LOCATION) );
927 else{
928 Set( P_LOCATION, 0, F_VALUE );
929 write( "Du hast keine Ortsangabe gesetzt.\n" );
930 }
931
932 return 1;
933}
934
935/** Setzt ICQ-UIN des Spielers (Spielerkommando).
936 * \param[in] str Spielereingabe
937 * \return 1 bei Erfolg, 0 sonst.
938 */
939static int set_icq(string str) {
940 int num;
941
942 if (!str || str=="") {
943 if (!num=QueryProp(P_ICQ))
944 write("Du hast keine ICQ-Nummer gesetzt!\n");
945 else
946 printf("Deine ICQ-Nummer lautet: %d\n",num);
947 return 1;
948 }
949 if (sscanf(str,"%d",num)!=1 || !num) {
950 write("Deine ICQ-Nummer wurde geloescht.\n");
951 SetProp(P_ICQ, 0);
952 } else {
953 write("Deine ICQ-Nummer wurde geaendert.\n");
954 printf("Sie lautet jetzt: %d\n",num);
955 SetProp(P_ICQ, num);
956 }
957 return 1;
958}
959
960/** Setzt Instant Messanger vom Spieler (Spielerkommando).
961 * \param[in] str Spielereingabe
962 * \return 1 bei Erfolg, 0 sonst.
963 */
964static int set_messenger(string str) {
965 int num;
966 string s;
967
968 if (!str || str=="") {
969 if (!s=QueryProp(P_MESSENGER))
970 if (!num=QueryProp(P_ICQ))
971 write("Du hast keine Messenger-ID gesetzt.\n");
972 else
973 printf("Du hast keine Messenger-ID gesetzt, aber eine ICQ-Nummer: %d\n", num);
974 else
975 printf("Deine Messenger-ID lautet: %s\n", s);
976 return 1;
977 }
978 if (str=="loeschen" || str=="keine") {
979 write("Deine Messenger-ID wurde geloescht.\n");
980 SetProp(P_MESSENGER, 0);
981 } else {
982 s = _unparsed_args();
983 printf("Deine Messenger-ID lautet nun: %s\n", s);
984 SetProp(P_MESSENGER, s);
985 }
986 return 1;
987}
988
989
990// Prueft, ob der String vermutlich eine eMail-Adresse ist.
991// dies ist nicht narrensicher, wird aber die meisten eMail-Adressen zulassen
992// und viel Schrott ablehnen.
993private string check_email(string str) {
994 if (!stringp(str)) return 0;
995 return regmatch(lower_case(str),
996 "[a-z0-9._%+-]+@(?:[a-z0-9-]+\.)+[a-z]{2,4}",RE_PCRE);
997}
998
999/** Setzt Email-Adresse des Spielers (Spielerkommando).
1000 * \param[in] str Spielereingabe
1001 * \return 1 bei Erfolg, 0 sonst.
1002 */
1003static int set_email(string str)
1004{
1005 if (!(str=_unparsed_args())) {
1006 write("Deine offizielle Email-Adresse lautet: " + QueryProp(P_MAILADDR)
1007 +"\n");
1008 return 1;
1009 }
1010 str = check_email(str);
1011 if (!str) {
1012 notify_fail("Deine Eingabe scheint keine gueltige EMail-Adresse "
1013 "zu sein.\n");
1014 return 0;
1015 }
1016 write("Deine EMail-Adresse wurde geaendert zu:\n"
1017 +str+"\n");
1018 SetProp(P_MAILADDR, str);
1019 return 1;
1020}
1021
1022/** Spielerkommando 'selbstloeschung'.
1023 * Gibt Meldung aus und fragt nach einer Bestaetigung.
1024 * \return 1 bei Erfolg, 0 sonst.
1025 */
1026static int self_delete()
1027{
Zesstra0dc75be2017-01-29 12:34:13 +01001028 string msg = sprintf("%s\n"
Zesstra54b691e2018-12-27 10:57:15 +01001029 "Wenn Du Dich selbstloeschen willst, ist Dein Charakter UNWIDERRUFLICH "
1030 "verloren. Es gibt KEINE Moeglichkeit, ihn wiederzuerschaffen. Solltest "
1031 "Du nur zeitweilig vom "MUDNAME" wegbleiben wollen, so benutze bitte "
MG Mud User88f12472016-06-24 23:31:02 +02001032 "den Befehl 'spielpause'.\n"+
Arathorne4d26072023-01-09 19:39:18 +01001033 "Falls Du %s immer noch selbstloeschen willst, gib Dein Passwort "
Zesstra0dc75be2017-01-29 12:34:13 +01001034 "ein.\n\n",
1035 (QueryProp(P_NO_ASCII_ART) ? "Bist Du Dir wirklich sicher?\n"
1036 : " 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 +02001037 capitalize(query_real_name()));
Zesstra0dc75be2017-01-29 12:34:13 +01001038 write(break_string(msg,78,0,BS_LEAVE_MY_LFS));
Zaphobf67f7192022-08-11 12:00:31 +02001039 input_to("self_delete2",INPUT_PROMPT|INPUT_NOECHO, "Bitte das Passwort angeben: ");
MG Mud User88f12472016-06-24 23:31:02 +02001040 return 1;
1041}
1042
1043/** Spielerkommando 'selbstloeschung'.
1044 * Empfaengt Bestaetigung des Spielers und ruft Loeschfunktion in
1045 * /secure/master auf.
1046 * \param[in] str Spielereingabe
1047 * \return 1 bei Erfolg, 0 sonst.
1048 */
1049int self_delete2(string str)
1050{
1051 int ret;
Zesstra04f613c2019-11-27 23:32:54 +01001052 ret=({int})"secure/master"->delete_player(str, getuid(PL));
MG Mud User88f12472016-06-24 23:31:02 +02001053 if (!ret)
1054 {
Zesstra7fd01782019-02-04 21:54:40 +01001055 write("Das hat nicht hingehauen (Jof sei Dank ....)\n");
MG Mud User88f12472016-06-24 23:31:02 +02001056 return 1;
1057 }
1058 if (QueryProp(P_GUILD)&&file_size(GUILD_DIR+QueryProp(P_GUILD)+".c")>-1)
1059 catch(call_other(GUILD_DIR+QueryProp(P_GUILD), "austreten");publish);
1060
1061 if (QueryProp(P_DEADS) < 5) {
1062 write("Adios! Man sieht sich.\n");
1063 say(name(WER,1)+" hat sich gerade selbst zerstoert.\n");
1064 }
1065 else {
1066 write(
1067 "\nTod kommt auf seinem weissen Pferd angeritten.\n"
1068 +"Er steigt ab, baut sich drohend vor Dir auf und mustert Dich schadenfroh.\n"
1069 +"\nTod sagt: ENDLICH! NUN KANN DIR AUCH LARS NICHT MEHR HELFEN!\n"
1070 +"\nTod holt weit mit seiner Sense aus. Mit grossem Schwung laesst er sie auf\n"
1071 +"Dich zusausen und dann...\n");
1072 say(name(WER,1)+" schied gerade endgueltig von uns.\n");
1073 }
1074
1075 // Event ausloesen. ;-)
1076 EVENTD->TriggerEvent(EVT_LIB_PLAYER_DELETION, ([
1077 E_PLNAME: getuid(ME),
1078 E_ENVIRONMENT: environment(),
1079 E_GUILDNAME: QueryProp(P_GUILD) ]) );
1080
1081 remove(1);
1082 return 1;
1083}
1084
1085/** Setzt neue taegliche Spieldauer (Spielerkommando).
1086 * \param[in] str Spielereingabe
1087 * \return 1 bei Erfolg, 0 sonst.
1088 */
1089static int spieldauer(string str) {
1090 int min,day;
MG Mud User88f12472016-06-24 23:31:02 +02001091
1092 notify_fail(" spieldauer <x> minuten fuer %d tage\noder\n"+
1093 " spieldauer <x> stunden fuer %d tage\n");
1094 if (!str)
1095 return 0;
Arathorn5513dfc2021-03-16 21:52:20 +01001096 if (sscanf(str,"%d stunde%~s fuer %d tag%~s",min,day)==4)
MG Mud User88f12472016-06-24 23:31:02 +02001097 min*=60;
Arathorn5513dfc2021-03-16 21:52:20 +01001098 else if (sscanf(str,"%d minute%~s fuer %d tag%~s",min,day)!=4)
MG Mud User88f12472016-06-24 23:31:02 +02001099 return 0;
1100 if (min<5)
1101 min=5;
1102 if (min>=1440)
1103 return notify_fail("Witzbold.\n"),0;
1104
1105 Set(P_DAILY_PLAYTIME,
1106 ({min*60,time()/86400+day,time()/86400,time(),min*60}));
1107 // 0:Minuten pro Tag, 1:Nr. des letzen Tages, 2:Nr. des angefangenen Tages,
1108 // 3:letzte Zeitpruefung, 4:verbleibende Zeit
1109 printf("Du darfst die naechsten %d Tag(e) nur noch\n"+
1110 "%d Minuten am Tag mudden.\n",day,min);
1111 return 1;
1112}
1113
1114/** Interpretiert Angabe des Spielers fuer Spielpause.
1115 * \param[in] a Zeitangabe fuer Spielpause.
1116 * \return Zeitpunkt, Ende der Spielpause.
1117 */
1118private int InterpretTime(string a){
1119 // akzeptiert folgende Formate:
1120 // dd.mm.jj (Rueckgabe: 0:00 des entsprechenden Tages)
1121
1122 int *ts = allocate(9);
1123 int i,j,k,nrargs;
1124
1125 if ((nrargs=sscanf(a,"%d.%d.%d",i,j,k))==3 ||
1126 (nrargs=sscanf(a,"%d.%d.",i,j))==2) {
1127 // wenn kein jahr angegeben ist, das aktuelle nehmen.
1128 if (nrargs == 2)
1129 ts[TM_YEAR] = localtime()[TM_YEAR];
1130 else {
1131 // Zwei-Ziffern-Angabe des Jahres...
1132 if (k<100)
1133 k += 2000;
1134 ts[TM_YEAR] = k;
1135 }
1136 ts[TM_MDAY] = i;
1137 ts[TM_MON] = j - 1;
1138
1139 int zeit = mktime(ts);
1140
1141 // negative und vergangene Zeiten pruefen.
1142 if (zeit <= time()) {
1143 write("Dieser Zeitpunkt liegt in der Vergangenheit.\n");
1144 return 0;
1145 }
1146 return zeit;
1147 }
1148 return 0;
1149}
1150
1151/** Setzt neue Spielpause (Spielerkommando).
1152 * Fragt vorher nach Bestaetigung durch den Spieler.
1153 * \param[in] str Spielereingabe
1154 * \return 1 bei Erfolg, 0 sonst.
1155 * \sa spielpause2()
1156 */
1157static int spielpause(string str)
1158{
1159 int days,endezeit;
1160 string foo;
1161
1162 notify_fail("spielpause <x> tage oder\n"+
1163 "spielpause bis tt.mm[.jj]\n");
1164 if (!str) return 0;
1165 if(sscanf(_unparsed_args(),"bis %s",foo)==1) {
1166 endezeit = InterpretTime(foo);
1167 if (endezeit == 0)
1168 return 0;
1169 days = ((endezeit - time()) / 86400) + 1;
1170 }
1171 else if(sscanf(str, "%d tag%s", days, foo) == 2) {
1172 if (days < 0)
1173 days = -1;
1174 else
1175 endezeit = (time()/86400) * 86400 + days * 86400;
1176 }
1177 else return 0;
1178
1179 if (days > 0)
1180 write(strftime("Du wirst Dich erst wieder am %d.%m.%Y einloggen koennen!\n",
1181 endezeit));
1182 else if (days < 0)
1183 write( "Du wirst Dich auf unbestimmte Zeit nicht mehr einloggen koennen.\n"
1184 +"Wenn Du wieder spielen willst, musst Du Dich an einen Gott oder\n"
1185 +"Erzmagier wenden (mit einem Gast oder Mail von aussen).\n" );
1186 else {
1187 write( "Die Spielpause ist aufgehoben.\n" );
1188 master()->TBanishName(getuid(this_object()), 0);
1189 return 1;
1190 }
1191 write( "Wenn Du das wirklich willst, gib jetzt 'ja' ein.\n" );
1192 input_to( "spielpause2", INPUT_PROMPT, "]", days);
1193 return 1;
1194}
1195
1196/** Setzt neue taegliche Spieldauer, wird ueber spielpause() gerufen.
1197 * \param[in] str Spielereingabe, Bestaetigung
1198 * \param[in] days Dauer der Spielpause (in Tagen).
1199 * \sa spielpause()
1200 */
1201static void spielpause2(string str, int days)
1202{
1203 if (str && (str == "ja" || str == "Ja" || str == "JA")) {
1204 master()->TBanishName(getuid(this_object()), days);
1205 write(
1206 "Ok, die Spielpause wird mit dem naechsten Ausloggen wirksam.\n"
1207 +"Solltest Du es Dir bis dahin noch einmal ueberlegt haben, so kannst\n"
1208 +"Du den Vorgang mit 'spielpause 0 tage' wieder rueckgaengig machen.\n" );
1209 return;
1210 }
1211 write("Vorgang wurde abgebrochen.\n" );
1212}
1213
1214/** Setzt neues Passwort (Spielerkommando).
1215 * Fragt nach altem Passwort und ruft change_password2().
1216 * \return 1 bei Erfolg, 0 sonst.
1217 * \sa change_password2(), change_password3(), change_password4()
1218 */
1219static int change_password() {
1220 string verb;
1221 verb=query_verb();
1222 if (verb!="passwd"&&verb!="password"&&verb!="passwort")
1223 return 0;
1224 input_to("change_password2",INPUT_NOECHO|INPUT_PROMPT,
1225 "Bitte das ALTE Passwort angeben: ");
1226 return 1;
1227}
1228
1229/** Setzt neues Passwort (Spielerkommando).
1230 * Prueft altes Passwort, fragt nach neuem Passwort und ruft
1231 * change_password3().
1232 * \param[in] str Spielereingabe des alten Passwortes
1233 * \return 1 bei Erfolg, 0 sonst.
1234 * \sa change_password(), change_password3(), change_password4()
1235 */
1236static int change_password2(string str) {
1237 write("\n");
1238 if (!str)
1239 str="";
1240 if (MASTER->update_password(str,str) == 0) {
1241 write("Falsches Passwort!\n");
1242 return 1;
1243 }
MG Mud User88f12472016-06-24 23:31:02 +02001244 input_to("change_password3",INPUT_NOECHO|INPUT_PROMPT,
Zesstrac780d7d2019-07-26 15:40:14 +02001245 "Bitte das NEUE Passwort eingeben: ", str);
MG Mud User88f12472016-06-24 23:31:02 +02001246 return 1;
1247}
1248
1249/** Setzt neues Passwort (Spielerkommando).
1250 * Prueft neues Passwort, fragt nach Bestaetigung des neues
1251 * Passwortes und ruft change_password4().
1252 * \param[in] str Spielereingabe des neuen Passwortes
1253 * \return 1 bei Erfolg, 0 sonst.
1254 * \sa change_password(), change_password2(), change_password4()
1255 */
Zesstrac780d7d2019-07-26 15:40:14 +02001256static int change_password3( string str, string passwold )
MG Mud User88f12472016-06-24 23:31:02 +02001257{
1258 write( "\n" );
1259
1260 if ( !str || str == "" ){
1261 write( "Abgebrochen !\n" );
MG Mud User88f12472016-06-24 23:31:02 +02001262 return 1;
1263 }
1264
1265 if ( passwold == str ){
1266 write( "Das war Dein altes Passwort.\n" );
1267 input_to( "change_password3", INPUT_NOECHO|INPUT_PROMPT,
Zesstrac780d7d2019-07-26 15:40:14 +02001268 "Bitte das NEUE Passwort eingeben (zum Abbruch Return "
1269 "druecken): ", passwold);
MG Mud User88f12472016-06-24 23:31:02 +02001270 return 1;
1271 }
1272
1273 if ( !MASTER->good_password( str, getuid(ME) ) ){
1274 input_to( "change_password3", INPUT_NOECHO|INPUT_PROMPT,
Zesstrac780d7d2019-07-26 15:40:14 +02001275 "Bitte das NEUE Passwort eingeben: ", passwold);
MG Mud User88f12472016-06-24 23:31:02 +02001276 return 1;
1277 }
1278
MG Mud User88f12472016-06-24 23:31:02 +02001279 input_to( "change_password4", INPUT_NOECHO|INPUT_PROMPT,
Zesstrac780d7d2019-07-26 15:40:14 +02001280 "Bitte nochmal: ", passwold, str);
MG Mud User88f12472016-06-24 23:31:02 +02001281 return 1;
1282}
1283
1284/** Setzt neues Passwort (Spielerkommando).
1285 * Prueft neues Passwort und setzt neues Passwort.
1286 * \param[in] str Spielereingabe des neuen Passwortes
1287 * \return 1 bei Erfolg, 0 sonst.
1288 * \sa change_password(), change_password2(), change_password3()
1289 */
Zesstrac780d7d2019-07-26 15:40:14 +02001290static int change_password4( string str, string passwold, string passwnew )
MG Mud User88f12472016-06-24 23:31:02 +02001291{
1292 write( "\n" );
1293
Zesstrac780d7d2019-07-26 15:40:14 +02001294 if ( !str || str != passwnew ){
MG Mud User88f12472016-06-24 23:31:02 +02001295 write( "Das war verschieden! Passwort NICHT geaendert.\n" );
MG Mud User88f12472016-06-24 23:31:02 +02001296 return 1;
1297 }
1298
Zesstrac780d7d2019-07-26 15:40:14 +02001299 if ( MASTER->update_password( passwold, passwnew ) )
MG Mud User88f12472016-06-24 23:31:02 +02001300 write( "Passwort geaendert.\n" );
1301 else
1302 write( "Hat nicht geklappt!\n" );
1303
MG Mud User88f12472016-06-24 23:31:02 +02001304 return 1;
1305}
1306
1307
1308/*
1309 *-----------------------------------------------------------------
1310 * Rueckmeldungen von Spielern an Magier
1311 *-----------------------------------------------------------------
1312 */
1313static int fehlerhilfe(string str) {
Bugfixa75344d2017-06-16 14:04:48 +02001314 ReceiveMsg("Welche Art von Fehler moechtest Du denn melden?\n"
MG Mud User88f12472016-06-24 23:31:02 +02001315 "Fehlfunktionen -> bug\n"
1316 "Ideen/Anregungen -> idee\n"
1317 "Tippfehler/Typos -> typo\n"
Bugfixa75344d2017-06-16 14:04:48 +02001318 "fehlende Details -> detail\n"
1319 "Syntax-Probleme -> syntaxhinweis",
1320 MT_NOTIFICATION|MSG_BS_LEAVE_LFS);
MG Mud User88f12472016-06-24 23:31:02 +02001321
1322 return 1;
1323}
1324
Arathorn3437e392016-08-26 22:41:39 +02001325static varargs int ReportError2(string player_input, string error_type)
1326{
1327 if ( stringp(player_input) && sizeof(player_input) &&
1328 player_input != "~q" && player_input != ".." )
1329 {
1330 object obj;
1331 // Eingabe am : aufsplitten, da dieser als Trennzeichen verwendet wird,
1332 // wenn sich die Eingabe auf ein bestimmtes Objekt beziehen soll. Das
1333 // erste Element im Ergebnisarray wird dann als dessen ID aufgefasst,
1334 // nach der weiter unten gesucht wird.
1335 // Zusaetzlich Whitespace drumherum wegschneiden.
1336 string *input_segments = regexplode(player_input,
1337 "[[:blank:]]*:[[:blank:]]*", RE_OMIT_DELIM | RE_PCRE);
1338
1339 // Wenn mind. 2 Segmente erzeugt wurden, soll offenbar ein bestimmtes
1340 // Objekt angesprochen werden, so dass dieses nun gesucht werden kann.
1341 if ( sizeof(input_segments) > 1 )
1342 {
1343 // Findet sich hinter dem ersten : nur ein Leerstring, wird davon
1344 // ausgegangen, dass da keine Meldung kam.
1345 // Fuer seltene Sonderfaelle wie mehrfache ":" wird noch geprueft, ob
1346 // von der rekonstruierten Meldung abzueglich der ":" ein Leerstring
1347 // uebrigbleibt. Wenn ja, war es wohl wirklich eine unbrauchbare
1348 // Meldung, und es wird eine neue angefordert.
1349 if ( input_segments[1] == "" &&
1350 implode(input_segments[1..],"") == "" )
1351 {
1352 _notify("Du hast hinter dem : nix eingegeben, bitte nochmal "
1353 "versuchen.\n", MA_UNKNOWN);
1354 // Eine neue Eingabe wird aber nur angefordert, wenn der aktuelle
1355 // Input ohnehin schon aus einem input_to()-Durchlauf stammte.
1356 // In diesem Fall ist extern_call() wahr.
1357 if ( extern_call() )
1358 input_to("ReportError2", INPUT_PROMPT, "]", error_type);
1359 return 1;
1360 }
1361
1362 // ID kleinschreiben, denn nur eine solche kann von Spielern eingegeben
1363 // werden, um ein Objekt anzusprechen, wir haben aber die Eingabe
1364 // per _unparsed_args(0) geholt, d.h. ggf. mit Grossbuchstaben.
1365 string obnam = lower_case(input_segments[0]);
Zesstra6da96cc2019-06-12 23:32:21 +02001366 if (obnam == "hier" || obnam == "raum")
1367 obj = environment(this_player());
1368 else
1369 {
1370 // Und das Objekt suchen, dabei zuerst im env() schauen. Nur present()
1371 // ginge auch, wuerde aber zuerst im Inv suchen, was aber nicht
1372 // gewuenscht ist.
1373 obj = present(obnam, environment(this_object())) || present(obnam);
1374 }
Arathorn3437e392016-08-26 22:41:39 +02001375
1376 // Wenn das Objekt gefunden wird, wird nur der Teil hinter dem ersten
1377 // : rekonstruiert und als Fehlermeldung abgesetzt.
1378 // Gibt es ein solches Objekt nicht, wird der Raum als Fallback
1379 // verwendet. Die Eingabe vor dem : gehoerte dann offenbar zur Meldung
1380 // dazu, und es wird dann spaeter die gesamte Spielereingabe im Original
1381 // als Fehlermeldung abgesetzt.
1382 if ( objectp(obj) )
1383 player_input = implode(input_segments[1..],":");
1384 else
1385 obj = environment(this_object());
1386 }
1387 else
1388 {
1389 // Hat der Spieler keinen : verwendet, verwenden wir das Bezugsobjekt
1390 // oder den aktuellen Raum.
1391 obj = QueryProp(P_REFERENCE_OBJECT);
1392 if (!objectp(obj) || !present(obj))
1393 obj = environment(this_interactive());
1394 }
Arathorn3437e392016-08-26 22:41:39 +02001395 smart_log(error_type, player_input, obj);
MG Mud User88f12472016-06-24 23:31:02 +02001396 }
Arathorn3437e392016-08-26 22:41:39 +02001397 else
1398 _notify("Eingabe abgebrochen.\n", MA_UNKNOWN);
MG Mud User88f12472016-06-24 23:31:02 +02001399 return 1;
1400}
1401
Arathorn3437e392016-08-26 22:41:39 +02001402static int ReportError(string player_input)
1403{
1404 // ungeparstes Kommando einlesen, um die Meldung unmodifiziert zu erhalten
1405 player_input = _unparsed_args(0);
1406 string pl_msg, error_type;
MG Mud User88f12472016-06-24 23:31:02 +02001407
Arathorn3437e392016-08-26 22:41:39 +02001408 // Anhand des eingegebenen Kommandoverbs wird der Typ der Fehlermeldung
1409 // ermittelt.
1410 switch(query_verb())
1411 {
1412 case "idee":
1413 pl_msg = "Was fuer eine Idee hast Du denn?\n";
1414 error_type = "IDEA";
1415 break;
1416 case "md":
1417 case "detail":
1418 pl_msg = "Fuer welches Detail fehlt denn die Beschreibung?\n";
1419 error_type = "DETAILS";
1420 break;
1421 case "typo":
1422 pl_msg = "Wo ist denn der Tippfehler?\n";
1423 error_type = "TYPO";
1424 break;
1425 case "bug":
1426 pl_msg = "Wie sieht der Fehler denn aus?\n";
1427 error_type = "BUGS";
1428 break;
Bugfixa75344d2017-06-16 14:04:48 +02001429 case "syntaxhinweis":
1430 pl_msg = "Mit welcher Syntax gibt es denn was fuer Probleme?\n";
1431 error_type = "SYNTAX";
1432 break;
MG Mud User88f12472016-06-24 23:31:02 +02001433 }
MG Mud User88f12472016-06-24 23:31:02 +02001434
Arathorn3437e392016-08-26 22:41:39 +02001435 // Hat der Spieler etwas eingegeben, wird die Eingabe direkt an die Hilfs-
1436 // funktion weitergereicht. Ansonsten wird eine Meldung ausgegeben und die
1437 // Eingabe manuell per input_to() abgefragt.
1438 if ( stringp(player_input) && sizeof(player_input) )
1439 {
1440 return ReportError2(player_input, error_type);
MG Mud User88f12472016-06-24 23:31:02 +02001441 }
Arathorn3437e392016-08-26 22:41:39 +02001442 ReceiveMsg(pl_msg, MT_NOTIFICATION);
1443 input_to("ReportError2", INPUT_PROMPT, "]", error_type);
MG Mud User88f12472016-06-24 23:31:02 +02001444 return 1;
1445}
1446
Zesstrad8331fe2021-02-13 19:04:21 +01001447static void confirm_error(string answer, object obj, string str,
1448 string myname, string desc, mapping err)
1449{
1450 if (answer != "j" && answer != "ja")
1451 {
1452 _notify("Eingabe abgebrochen.\n", MA_UNKNOWN);
1453 return;
1454 }
1455 if (!obj)
1456 {
1457 _notify(sprintf("Leider existiert das Objekt nicht mehr, an dem Du "
1458 "D%s melden wolltest.", desc), MA_UNKNOWN);
1459 return;
1460 }
1461
1462 // ggf. will das Objekte selber loggen, dann wird nicht zentral geloggt.
1463 if (obj->SmartLog(0, myname, str, strftime("%d. %b %Y")))
1464 {
1465 _notify(sprintf(
1466 "Du hast an %s erfolgreich %s abgesetzt.\n"
1467 "Hinweis: Das Objekt selber hat die Meldung protokolliert.",
1468 (obj->IsRoom() ? "diesem Raum" : obj->name(WEM,1)),desc),
1469 MA_UNKNOWN);
1470 }
1471 else
1472 {
1473 // Eintragung in die Fehler-DB
1474 string hashkey = ({string})ERRORD->LogReportedError(err);
1475 _notify(sprintf(
1476 "Ein kleiner Fehlerteufel hat D%s an %s unter der ID %s "
1477 "notiert.", desc,
1478 (obj->IsRoom() ? "diesem Raum" : obj->name(WEM,1)),
1479 hashkey || "N/A"),
1480 MA_UNKNOWN);
1481 }
1482 _notify("Vielen Dank fuer die Hilfe.\n", MA_UNKNOWN);
1483}
1484
MG Mud User88f12472016-06-24 23:31:02 +02001485/** Loggt eine Spielermeldung an Magier.
1486 * Loggt die Spielermeldung in das passende File unter /log/report/ oder im
1487 * vom Magier gewuenschten File. Hierbei werden Fehler, Ideen, MDs und Typos
1488 * in getrennte Files sortiert.
1489 * \param[in] str Spielermeldung
1490 * \param[in] myname Art der Spielermeldung (DETAILS, BUG, TYPO, MD)
1491 * @see md(string), idea(string), bug(string), typo(string)
1492 */
Zesstra42594f82019-11-11 21:07:02 +01001493protected void smart_log(string myname, string str, object obj)
MG Mud User88f12472016-06-24 23:31:02 +02001494{
MG Mud User88f12472016-06-24 23:31:02 +02001495 mapping err = ([ F_PROG: "unbekannt",
1496 F_LINE: 0,
1497 F_MSG: str,
1498 F_OBJ: obj
1499 ]);
1500
1501 string desc="etwas unbekanntes";
1502 switch(myname) {
1503 case "BUGS":
1504 desc="einen Fehler";
1505 err[F_TYPE]=T_REPORTED_ERR;
1506 break;
1507 case "DETAILS":
1508 desc="ein fehlendes Detail";
1509 err[F_TYPE]=T_REPORTED_MD;
1510 break;
1511 case "IDEA":
1512 desc="eine Idee";
1513 err[F_TYPE]=T_REPORTED_IDEA;
1514 break;
1515 case "TYPO":
1516 desc="einen Typo";
1517 err[F_TYPE]=T_REPORTED_TYPO;
1518 break;
Bugfixa75344d2017-06-16 14:04:48 +02001519 case "SYNTAX":
1520 desc="einen Syntaxhinweis";
1521 err[F_TYPE]=T_REPORTED_SYNTAX;
1522 break;
MG Mud User88f12472016-06-24 23:31:02 +02001523 }
Zesstrad8331fe2021-02-13 19:04:21 +01001524 _notify(sprintf(
1525 "Du hast %s an %s mit der Beschreibung \"%s\" eingegeben. "
Bugfixa8148802022-09-25 19:40:21 +02001526 "Wurde das richtige Zielobjekt ausgewaehlt und moechtest Du "
1527 "speichern?", desc,
Zesstra9c0bd262019-12-03 19:04:45 +01001528 (obj->IsRoom() ? "diesem Raum" : obj->name(WEM,1)),
Zesstrad8331fe2021-02-13 19:04:21 +01001529 str), MA_UNKNOWN);
1530 input_to("confirm_error", INPUT_PROMPT, "]", obj, str, myname, desc, err);
MG Mud User88f12472016-06-24 23:31:02 +02001531}
1532
1533/** Speichert den Spieler und loggt ihn aus (Spielerkommando 'ende').
1534 * Der Spieler wird vollstaendig ausgeloggt, d.h. das Spielerobjekt
1535 * zerstoert.
1536 * \return 1 bei Erfolg, 0 sonst.
1537 * @see disconnect()
1538 */
1539int quit()
1540{
MG Mud User88f12472016-06-24 23:31:02 +02001541 SetProp(P_LAST_QUIT,time());
1542 catch(RemoveChannels();publish);
1543 if(!QueryGuest())
1544 {
1545 save_me(0);
1546 tell_object(ME,"Speichere "+QueryProp(P_NAME)+".\n");
1547 }
1548
MG Mud User88f12472016-06-24 23:31:02 +02001549 remove_living_name();
Zesstra9ea7e9a2021-11-01 11:33:34 +01001550
1551 // call_notify_player_change wird in remove() gerufen.
1552
1553 if(catch(remove();publish))
1554 destruct(ME);
1555
MG Mud User88f12472016-06-24 23:31:02 +02001556 return 1;
1557}
1558
1559/** Wrapper im quit() herum, verhindert 'ende', falls Spieler kaempft.
Bugfix8f4df942022-01-19 13:39:56 +01001560 * Wenn der Spieler unter Level 10 ist, wird gefragt, ob er sich wirklich
1561 * mit Ende ausloggen will, um Verwechslungen mit schlafe ein zu vermeiden.
1562 * \return 0 bei Abbruch wegen Kampf, 1 bei Nachfrage per ask_quit(), sonst Rueckgabewert von quit()
1563 * @see quit(), ask_quit()
MG Mud User88f12472016-06-24 23:31:02 +02001564 */
1565static int new_quit() {
1566 notify_fail("Du bist in Gedanken noch bei Deinem letzten Kampf.\n"+
1567 "Warte noch etwas bevor Du das Spiel verlaesst,\n"+
1568 "damit Du so nicht in RL weitermachst...\n");
1569 if (time()-Query(P_LAST_COMBAT_TIME)<120 && !IS_LEARNING(ME))
1570 return 0;
Bugfix8f4df942022-01-19 13:39:56 +01001571 // Absicherung fuer kleine Spielern gegen Verwechslung mit schlafe ein
1572 if(QueryProp(P_LEVEL) < 10)
1573 {
1574 write(break_string(
1575 "Moechtest Du wirklich \"ende\" benutzen?\n"
Arathorne4d26072023-01-09 19:39:18 +01001576 "Du verlierst Deine Ausruestung und beginnst wieder an Deinem "
Bugfix8f4df942022-01-19 13:39:56 +01001577 "Rassenstartpunkt. \n"
Arathorne4d26072023-01-09 19:39:18 +01001578 "Wenn Du Dich einfach nur ausloggen und spaeter weiter spielen "
Bugfix8f4df942022-01-19 13:39:56 +01001579 "willst, dann benutze besser \"schlafe ein\".\n\n", 78, 0,
1580 BS_LEAVE_MY_LFS));
1581 input_to(
1582 function void (string str) {
1583 if(str != "ja" && str != "Ja")
1584 write("Ok, \"ende\" wird nicht ausgefuehrt.\n");
1585 else
1586 quit();
1587 },
1588 INPUT_PROMPT,
1589 "Mittels \"ende\" ausloggen? (ja/nein):");
1590 return 1;
1591 }
MG Mud User88f12472016-06-24 23:31:02 +02001592 return quit();
1593}
1594
1595/** Gibt die Infos ueber den Char an den Spieler aus (Spielerkommando 'info').
1596 * \param[in] arg Wenn arg=="short", wird eine Kurzuebersicht ausgegeben.
1597 * \return 1
1598 * @see short_score()
1599 */
1600static int score(string arg) {
Arathorn5513dfc2021-03-16 21:52:20 +01001601 int val;
MG Mud User88f12472016-06-24 23:31:02 +02001602 mixed ind;
1603 object *enem1, *enem2, *inv;
1604
1605 if (QueryProp(P_GHOST)) {
1606 write("Im ewigen Leben gibt es keine Punkte.\n");
1607 return 1;
1608 }
1609
1610 int plev = LEPMASTER->QueryLevel();
Zesstra0d1bd1d2019-11-23 10:19:15 +01001611 <string|int> tmp = QueryProp(P_GENDER);
1612 string gender;
1613 switch(tmp) {
MG Mud User88f12472016-06-24 23:31:02 +02001614 case MALE: gender = "maennlich"; break;
1615 case FEMALE: gender = "weiblich"; break;
1616 case NEUTER: gender = "neutral"; break;
Zesstra0d1bd1d2019-11-23 10:19:15 +01001617 default: gender = "unbekannt";
MG Mud User88f12472016-06-24 23:31:02 +02001618 }
1619
1620 ind = m_indices(QueryProp(P_ATTRIBUTES));
1621 tmp = "";
1622 foreach(string index: ind) {
1623 string aname;
1624 switch (index) {
1625 case "int": aname = "Intelligenz"; break;
1626 case "con": aname = "Ausdauer"; break;
1627 case "dex": aname = "Geschicklichkeit"; break;
1628 case "str": aname = "Kraft"; break;
1629 default:
1630 if(stringp(index)) aname = capitalize(index);
1631 else aname = "Unbekannt";
1632 }
1633 aname = sprintf("%-18'.'s %2.2d", aname+" ", QueryRealAttribute(index));
1634 if((val = QueryAttributeOffset(index)))
1635 aname += sprintf(" (%s%d)", (val>=0?"+":""), val);
1636 tmp += aname + "\n";
1637 }
1638
1639 printf("- %-'-'68s\n",
1640 TeamPrefix()+capitalize(implode(explode(short()||"","\n"),""))+" ");
1641 if(arg!="short") {
1642 printf("Rasse ............ %-' '18s Abenteuer ........ %d %s\n",
1643 QueryProp(P_RACE), QueryProp(P_QP),
1644 (val = QM->QueryTotalQP()) == QueryProp(P_QP) ? "" : "("+val+")");
1645 printf("Geschlecht ....... %-' '18s Groesse .......... %d cm\n",
1646 gender, QueryProp(P_SIZE));
1647 printf("Stufe ............ %-3.3d %-' '14s Gewicht .......... %d kg\n",
1648 QueryProp(P_LEVEL), (QueryProp(P_LEVEL) < plev ? "("+plev+")" : ""),
1649 QueryProp(P_WEIGHT) / 1000);
1650 printf("Gilde ............ %-' '18s Gildenstufe ...... %d\n",
1651 capitalize(QueryProp(P_GUILD)), QueryProp(P_GUILD_LEVEL));
1652 }
Rumatabd442262021-09-27 11:02:50 +02001653 printf("Erfahrung ........ %-' '18s Gesinnung ........ %-s\n\n",
MG Mud User88f12472016-06-24 23:31:02 +02001654 QueryProp(P_XP)+ " Punkte", al_to_title(QueryProp(P_ALIGN)));
1655 printf("%#-76.2s\n\n", tmp);
1656 printf("Gesundheit ....... %-3.3d %-' '14s Gift ............. %s\n",
1657 QueryProp(P_HP),
1658 (QueryProp(P_HP) == (val = QueryProp(P_MAX_HP)) ? "" : "("+val+")"),
1659 ((val = QueryProp(P_POISON)) ?
1660 (val < 4 ? "leicht" : "gefaehrlich") : "gesund"));
1661 printf("Konzentration .... %-3.3d %-' '14s Vorsicht ......... %s\n",
1662 QueryProp(P_SP),
1663 (QueryProp(P_SP) == (val = QueryProp(P_MAX_SP)) ? "" : "("+val+")"),
1664 ((ind = QueryProp(P_WIMPY)) ? ""+ind : "mutig"));
1665 printf("Todesfolgen....... %-' '18s %s\n",
1666 ((val = death_suffering()) ? ""+((val+9)/10) : "kein Malus"),
1667 (QueryProp(P_WIMPY) && ind=QueryProp(P_WIMPY_DIRECTION))
1668 ? sprintf("Fluchtrichtung ... %O", ind) : "");
1669 printf("%s",
1670 (time()-Query(P_LAST_COMBAT_TIME)<120 && !IS_LEARNING(ME)) ?
1671 "Spiel verlassen .. nicht moeglich\n" : ""
1672 );
1673
1674 if(arg!="short") {
1675 write(break_string(Forschung(), 70));
1676 if(ind=QueryProp(P_AWAY))
1677 printf("Du bist nicht ansprechbar: %O\n",ind);
1678 }
1679
Zesstra04f613c2019-11-27 23:32:54 +01001680 if(sizeof(enem1=(QueryEnemies())[0])) {
MG Mud User88f12472016-06-24 23:31:02 +02001681 enem2=({});
1682 inv=all_inventory(environment(ME));
1683 foreach(object en: enem1) {
1684 if (member(inv,en)==-1) // Ist unser Feind und ist nicht hier
1685 enem2+=({en});
1686 }
1687 if(sizeof(enem2))
1688 {
1689 write(break_string(
1690 "Du verfolgst " + CountUp(map_objects(enem2, "name", WEN))+".",
1691 78));
1692 }
1693 }
1694 if(arg!="short") show_age();
1695 printf("%-'-'70s\n", "");
1696 return 1;
1697}
1698
1699/** Gibt eine kuerzere Info ueber den Char aus (Spielerkommando punkte|score).
1700 Ruft score("short").
1701 * \param[in] arg UNUSED
1702 * \return 1 bei Erfolg, 0 sonst.
1703 * @see score(string), very_short_score()
1704 */
1705static int short_score(string arg) {
1706 return score("short");
1707}
1708
1709/** Gibt eine Miniinfo ueber LP / KP aus (Spielerkommando: kurzinfo)
1710 * \return 1
1711 * @see score(string), short_score(string)
1712 */
1713static int very_short_score(string arg) {
1714 int lp,mlp,xlp,kp,mkp,xkp;
1715 string bar;
1716
1717 lp=QueryProp(P_HP); mlp=QueryProp(P_MAX_HP);
1718 kp=QueryProp(P_SP); mkp=QueryProp(P_MAX_SP);
1719 if (mlp)
1720 xlp=(lp*40/mlp);
1721 if (mkp)
1722 xkp=(kp*40/mkp);
1723 bar=" . . . . . . . . ";
1724 if (QueryProp(P_NO_ASCII_ART) || arg == "-k")
1725 printf("Gesundheit: %3.3d (%3.3d), Konzentration: %3.3d (%3.3d)\n",
1726 lp, mlp, kp, mkp);
1727 else
1728 printf("Gesundheit: 0 |%'#'40.40s| %3.3d%s\n"+
1729 "Konzentration: 0 |%'#'40.40s| %3.3d%s\n",
1730 (xlp<0?bar:bar[xlp..]),lp,(lp==mlp?"":sprintf(" (%d)",mlp)),
1731 (xkp<0?bar:bar[xkp..]),kp,(kp==mkp?"":sprintf(" (%d)",mkp))
1732 );
1733 return 1;
1734}
1735
1736/** Gibt eine Manpage/Hilfeseite an den Spieler aus.
1737 Beruecksichtigt hierbei die Synonymliste aus dir/.synonym, um die richtige
1738 Manpage auszugeben.
1739 * \param[in] dir Verzeichnis der gewuenschten Manpage
1740 * \param[in] page Name der gewuenschten Manpage
1741 * \return String der gewuenschten Manpage
1742 */
1743static string getmanpage(string dir, string page)
1744{
1745 string text, *syn;
1746 int i;
1747
1748 if (dir[<1] != '/')
1749 dir += "/";
1750
1751 if ((text=read_file(dir+page)) && sizeof(text))
1752 return text;
1753
1754 if (text = read_file(dir+".synonym")) {
1755 syn = regexplode(text, "([ \t][ \t]*|\n)");
1756 if ((i=member(syn, page))!=-1)
1757 return read_file(dir+syn[i+2]);
1758 }
1759 return 0;
1760}
1761
1762/** Gibt eine Hilfeseite an den Spieler aus (Spielerkommando hilfe|man).
1763 * Die Hilfeseite wird in div. Verzeichnissen gesucht (je nach Gilde,
1764 * Magier-/Spielerstatus).
1765 * \param[in] str Name der gewuenschten Hilfeseite.
1766 * \return 1
1767 */
1768static int help(string str) {
1769 string verb, rest, text, gilde;
1770 mixed found;
1771
1772 found=0;
1773 text = "";
1774 if (str) {
1775 str = implode( explode(str, ".." ), "");
1776
1777 if ( sscanf( str, "gilde %s %s", gilde, rest)==2)
1778 str=rest;
1779 else
1780 gilde=QueryProp(P_GUILD);
1781 if (!gilde) gilde="abenteurer";
1782
1783 if ( sscanf( str, "%s %s",verb,rest )==2 ) str = verb;
1784
1785 if ((IS_LEARNER(PL)) ) {
1786 if (rest = getmanpage("/doc/wiz/",str)) {
1787 found = 1;
1788 text += rest;
1789 }
1790 else if (rest = getmanpage("/doc/mcmd/", str)) {
1791 found = 1;
1792 text += rest;
1793 }
1794 }
1795
1796 if ((IS_SEER(PL)) /*&& !found*/ ) {
1797 if (rest = getmanpage("/doc/scmd/",str)) {
1798 if (found)
1799 text += "\n--------------------\n";
1800 found = 1;
1801 text += rest;
1802 }
1803 }
1804
1805 if (rest = getmanpage("/doc/g."+gilde+"/",str)) {
1806 if (found)
1807 text += "\n--------------------\n";
1808 found = 1;
1809 text += rest;
1810 } else {
1811 if (rest = getmanpage("/doc/help/",str)) {
1812 if (found)
1813 text += "\n--------------------\n";
1814 found = 1;
1815 text += rest;
1816 }
1817 else if (rest = getmanpage("/doc/pcmd/",str)) {
1818 if (found)
1819 text += "\n--------------------\n";
1820 found = 1;
1821 text += rest;
1822 }
1823 else if (rest = getmanpage("/doc/REGELN/",str)) {
1824 if (found)
1825 text += "\n--------------------\n";
1826 found = 1;
1827 text += rest;
1828 }
1829 }
1830
1831 if (!found)
1832 text = "Dazu ist keine Hilfe verfuegbar.\n";
1833
1834 More(text,0);
1835 return 1;
1836 }
1837 if (IS_LEARNER(PL))
1838 text = read_file("/doc/hilfe.magier");
1839 else if (IS_SEER(PL))
1840 text = read_file("/doc/hilfe.seher");
1841
1842 More(text + read_file("/doc/hilfe.spieler"), 0);
1843 return 1;
1844}
1845
1846/** Ermittelt angebene Optionen fuer das Spielerkommando 'wer'.
1847 * \param[in] str vom Spieler spezifizierter String von Filteroptionen: -k,
1848 * -v, -a, -s (+ Langformen).
1849 * \return Array von Spieleroptionen als veroderte Int-Flags und Rest der
1850 * Spielereingabe ohne die Optionen.
1851 */
1852static mixed filter_who_options(string str)
1853{
Arathorn5513dfc2021-03-16 21:52:20 +01001854 string* opt;
MG Mud User88f12472016-06-24 23:31:02 +02001855 int i,len,res;
1856
1857 opt = explode(str," "); len=sizeof(opt);
MG Mud User88f12472016-06-24 23:31:02 +02001858 res = 0;
1859 for(i=0;i<len;i++)
1860 switch(opt[i]){
1861 case "-k":
1862 case "-kurz":
1863 res |= WHO_SHORT; break;
1864 case "-v":
1865 case "-vertikal":
1866 res |= WHO_VERTICAL; break;
1867 case "-alphabetisch":
1868 case "-a":
1869 case "-alpha":
1870 res |= WHO_ALPHA; break;
1871 case "-s":
1872 case "-spieler":
1873 res |= WHO_PLAYER_VIEW; break;
1874 default:
1875 return ({ res, implode(opt[i..]," ") });
1876 }
1877 return ({ res, 0 });
1878
1879}
1880
1881/** Spielerkommando 'wer', fragt /obj/werliste ab.
1882 * \param[in] str Spielereingabe mit Optionen fuer wer.
1883 * \return 1
1884 */
1885static int who(string str) {
1886 int i,shrt;
1887 string ret;
1888 mixed ans;
1889
1890 if ((str=_unparsed_args())&&str[0..0]!="-") {
1891 ans = filter_who_options(str);
1892 shrt = ans[0];
1893 str = ans[1];
1894 if (!shrt) {
1895 if (ret=INETD->_send_udp(str,
1896 ([ REQUEST: "who", SENDER: getuid(ME) ]), 1 ))
1897 write(ret);
1898 else
1899 write("Anfrage abgeschickt.\n");
1900 return 1;
1901 }
1902 }
1903 if (str) i=(member(str,'o')>0); else i=0;
1904 if (sizeof(str)>1 && str[0] == '-') str = str[1..1];
1905 More(implode( "/obj/werliste"->QueryWhoListe(
1906 IS_LEARNER(ME) && QueryProp(P_WANTS_TO_LEARN),shrt,0,str,i),"\n"),0);
1907 return 1;
1908}
1909
1910/** Spielerkommando 'kwer', fragt /obj/werliste ab.
1911 * \param[in] str Spielereingabe mit Optionen fuer wer.
1912 * \return 1
1913 */
1914static int kwho(string str)
1915{
1916 int shrt;
1917 mixed res;
1918
1919 if(str) {
1920 res = filter_who_options(str);
1921 shrt = res[0];
1922 str = res[1];
1923 }
1924 More(implode( "/obj/werliste"->QueryWhoListe(
1925 IS_LEARNER(ME) && QueryProp(P_WANTS_TO_LEARN), shrt|WHO_SHORT ,0,str),
1926 "\n")+"\n\n",0);
1927 return 1;
1928}
1929
1930/** Spielerkommando 'kkwer', gibt eine einfache Liste der Anwesenden aus.
1931 Filtert unsichtbare Spieler aus, falls SPielerobjekt kein Magier ist.
1932 * \param[in] str Spielereingabe mit Optionen fuer wer.
1933 * \return 1
1934 */
1935static varargs int kkwho(string str) {
1936 object *obs;
1937 string *namen;
1938
1939 obs=filter_users(str);
1940 namen=({});
1941 if (IS_LEARNER(this_player())) {
1942 foreach(object ob: obs) {
1943 if (environment(ob))
1944 namen+=({capitalize(geteuid(ob))});
1945 }
1946 }
1947 else {
1948 foreach(object ob: obs) {
1949 if (!ob->QueryProp(P_INVIS) && environment(ob))
1950 namen+=({capitalize(geteuid(ob))});
1951 }
1952 }
1953 if (sizeof(namen))
1954 write(break_string(CountUp(sort_array(namen,#'>),", ", ", ")+".",75));
1955 else
1956 write("Keine passenden Spieler gefunden.\n");
1957
1958 return 1;
1959}
1960
1961/** Spielerkommando 'toete'.
1962 * Prueft auf Geist, Gast, toten HC-Spieler, Waffe/freie Hand. Versucht einen
1963 * Gegner zu finden und ruft dann Kill(string).
1964 * \param[in] str Spielereingabe
1965 * \return 1 oder 0, falls kein potentieller Gegner bei 'toete alle' gefunden
1966 * wird.
1967 */
1968static int kill(string str) {
Arathorn5513dfc2021-03-16 21:52:20 +01001969 object eob;
MG Mud User88f12472016-06-24 23:31:02 +02001970
1971 if (QueryProp(P_GHOST))
1972 {
1973 write("Das kannst Du in Deinem immateriellen Zustand nicht.\n");
1974 return 1;
1975 }
1976
1977 if(hc_play>1)
1978 {
1979 write("DAS HAST DU HINTER DIR.\n");
1980 return 1;
1981 }
1982
1983 if (QueryGuest())
1984 {
1985 write("Du bist doch nur Gast hier.\n");
1986 return 1;
1987 }
1988 if (!str || str == "") {
1989 write("WEN willst Du toeten?\n");
1990 return 1;
1991 }
1992 if( !QueryProp(P_WEAPON) && QueryProp(P_FREE_HANDS)==0 ) {
1993 write(
1994 "Dazu solltest Du eine Waffe gezueckt oder eine Hand frei haben.\n");
1995 return 1;
1996 }
1997 str=lower_case(str);
1998 if (str=="alle") {
Arathornf499db72019-11-25 21:30:42 +01001999 object* livs = filter(all_inventory(environment(PL)),
MG Mud User88f12472016-06-24 23:31:02 +02002000 function int (object ob) {
2001 if (living(ob) && !query_once_interactive(ob)
2002 && !ob->QueryProp(P_INVIS)
2003 && !ob->QueryProp(P_NO_GLOBAL_ATTACK)
2004 && !ob->QueryProp(P_FRIEND))
2005 {
2006 Kill(ob);
2007 return 1;
2008 }
2009 return 0;
2010 } );
2011 // wenn Gegner gefunden, raus, ansonsten kommt die Fehlermeldung unten.
2012 if (sizeof(livs)) return 1;
2013 }
2014 else {
2015 int i=1;
2016 while(objectp(eob = present(str,i++,environment(PL)))) {
2017 if (living(eob) && !eob->QueryProp(P_INVIS))
2018 break;
2019 else
2020 eob=0;
2021 }
2022 }
2023 if (!objectp(eob)) {
2024 // per write und return 1 ist hier mal ok, weil dies Kommando im Spieler
2025 // eh zuletzt in der Kommandokette ausgefuehrt wird und per return 0 eh
2026 // kein anderes mehr zum Zug kommt.
2027 write("Du siehst hier kein derartiges Wesen!\n");
2028 return 1;
2029 }
2030 else if (eob == PL) {
2031 write("Selbstmord ist keine Loesung!\n");
2032 return 1;
2033 }
2034
2035 /* Kill him */
2036 Kill(eob);
2037 return 1;
2038}
2039
2040/** Spielerkommando 'stop'.
2041 * Loescht die Gegnerliste, sofern man nicht InFight() ist.
2042 * \param[in] str Spielereingabe, wird ignoriert.
2043 * \return 1
2044 */
2045static int stop( string str )
2046{
2047 if ( InFight() ){
2048 write( "Das geht nicht mitten im Kampf.\n" );
2049 return 1;
2050 }
2051
2052 if ( !str ){
2053 StopHuntingMode();
2054 write( "Ok.\n" );
2055 return 1;
2056 }
2057
2058 if ( !StopHuntID(str) )
2059 write( "So jemanden verfolgst Du nicht!\n" );
2060
2061 return 1;
2062}
2063
2064/** Spielerkommando fuers emoten ':'.
2065 * \param[in] str Spielereingabe
2066 * \param[in] genitiv Genetivflag
2067 * \return 1 oder 0, falls Spieler nicht emoten kann (kein CAN_EMOTE Flag in
2068 * P_CAN_FLAGS).
2069 */
2070int emote(string str,int genitiv)
2071{
2072 string *commands,message,verb;
2073 object living;
Arathorn5513dfc2021-03-16 21:52:20 +01002074 int size;
MG Mud User88f12472016-06-24 23:31:02 +02002075
2076 if (!(Query(P_CAN_FLAGS)&CAN_EMOTE)) return 0;
2077 if (query_verb()[0]==';') genitiv=1;
2078 if (query_verb()[0]==':'||query_verb()[0]==';')
2079 verb=query_verb()[1..]+" ";
2080 else
2081 verb="";
2082 str=this_player()->_unparsed_args();
2083 commands=explode(verb+(str||""),"#");
2084 message=break_string((IS_SEER(ME) ? "" : ">")
2085 +capitalize(genitiv ? name(WESSEN) :
2086 name())
2087 +" "+commands[0],78);
2088 size=sizeof(commands);
2089 if(size>=3)
2090 {
2091 living=find_living(lower_case(commands[1]));
2092 if(!living || environment(living)!=environment() ||
2093 (living->QueryProp(P_INVIS)) && !IS_LEARNER(ME))
2094 {
2095 write(capitalize(commands[1])+" sehe ich hier nicht!\n");
2096 return 1;
2097 }
2098 if(living!=this_object())
2099 tell_object(living,break_string((IS_SEER(this_player()) ? "" : ">")
2100 +capitalize(genitiv ?
2101 this_player()->name(WESSEN) :
2102 this_player()->name())
2103 +" "+commands[2],78));
2104 }
2105 if(size>=4)
2106 write(break_string(commands[3],78));
2107 else
2108 write(message);
2109 tell_room(environment(),message,({this_object(),living}));
2110 return 1;
2111}
2112
2113/** Spielerkommando fuers remoten 'r:'.
2114 * \param[in] str Spielereingabe
2115 * \param[in] flag Genetivflag
2116 * \return 1 oder 0, falls Spieler nicht emoten kann (kein CAN_REMOTE Flag in
2117 * P_CAN_FLAGS).
2118 */
2119static int remote(string str, int flag)
2120{
2121 int m;
2122 string tmp, dest;
2123 string *exstr;
2124 object destpl;
2125
2126 if ( !(Query(P_CAN_FLAGS) & CAN_REMOTE) )
2127 return 0;
2128
2129 if ( !(str=_unparsed_args()) ||
2130 sizeof( (exstr=explode(str," ")) - ({""}) ) <= 1 ){
2131 write("Was willst Du zu wem `emoten`?\n");
2132 return 1;
2133 }
2134
2135 dest = lower_case(exstr[0]);
2136
2137 if( !(destpl=find_player( dest ) ) ||
2138 (destpl->QueryProp(P_INVIS) && !IS_LEARNER(ME)) ){
2139 write("Einen solchen Spieler gibt es derzeit nicht.\n");
2140 return 1;
2141 }
2142
2143 tmp = implode( exstr[1..], " " );
2144
2145 tmp = regreplace( tmp, "(^|[^#])#($|[^#])", "\\1aus der Ferne\\2", 1 );
2146 tmp = regreplace( tmp, "(^|[^\\^])\\^($|[^\\^])", "\\1in der Ferne\\2", 1 );
2147 tmp = regreplace( tmp, "##", "#", 1 );
2148 tmp = regreplace( tmp, "\\^\\^", "^", 1 );
2149
2150 if ( strstr( tmp, "aus der Ferne" ) == -1
2151 && strstr( tmp, "in der Ferne" ) == -1 )
2152 tmp += " aus der Ferne";
2153
2154 if ( QueryProp(P_INVIS) && IS_LEARNER(destpl) ){
2155 str = "(" + capitalize(getuid(ME));
2156 if ( flag )
2157 str += member( "sxz", str[<1] ) == -1 ? "s" : "'";
2158 str += ")";
2159 }
2160 else
2161 str = (flag ? capitalize(name(WESSEN)) : capitalize(name(WER)));
2162
2163 str += " " + tmp + (member( ".?!", tmp[<1] ) == -1 ? "." : "") + "\n";
2164
2165 m = destpl->ReceiveMsg(str, MT_COMM|MT_FAR, MA_EMOTE,0, ME);
2166 switch(m)
2167 {
2168 case MSG_DELIVERED:
Bugfix70d66a52017-03-06 14:27:35 +01002169 // Als origin muss der Empfaenger uebergeben werden, sonst kann die
2170 // Meldung in der tmhist nicht richtig zugeordnet werden.
Zesstra4eb67dc2016-12-17 19:56:31 +01002171 ReceiveMsg(capitalize(destpl->name()) + "->" + str, MT_COMM|MT_FAR,
Bugfix70d66a52017-03-06 14:27:35 +01002172 MA_EMOTE, 0, destpl);
MG Mud User88f12472016-06-24 23:31:02 +02002173 break;
2174 case MSG_BUFFERED:
2175 write( capitalize(destpl->name(WER) + " ist gerade beschaeftigt.\n") );
2176 break;
2177 case MSG_IGNORED:
2178 write( capitalize(destpl->name(WER) + " ignoriert Dich.\n") );
2179 break;
2180 case MSG_VERB_IGN:
2181 case MSG_MUD_IGN:
2182 write( capitalize(destpl->name(WER) + " ignoriert Deine Meldung.\n") );
2183 break;
2184 default:
2185 write( capitalize(destpl->name(WER) + " kann Dich gerade nicht "
2186 "wahrnehmen.\n") );
2187 }
2188 return 1;
2189}
2190
2191/** Spielerkommando fuers emoten im Genitiv ';'.
2192 * Ruft emote(string,int) auf.
2193 */
2194static int gemote(string str)
2195{
2196 return emote(str, 1);
2197}
2198
2199/** Spielerkommando fuers remoten im Genitiv 'r;'.
2200 * Ruft remote(string, int) auf.
2201 */
2202static int gremote(string str)
2203{
2204 return remote(str, 1);
2205}
2206
2207static void load_auto_objects(mapping map_ldfied);
2208
2209private void InitPlayer();
2210private void InitPlayer2();
2211private void InitPlayer3();
2212
2213/** Gibt eine Zufallszahl um P_AVERAGE_SIZE herum zurueck.
2214 * \return Zufaellige Groesse.
2215 */
2216private int RandomSize()
2217{
2218 return (100+random(13)-random(13)+random(13)-random(13))*
2219 (QueryProp(P_AVERAGE_SIZE)||170)/100;
2220}
2221
2222/** Setzt bestimmte Props im Spieler, falls diese nicht gesetzt sind oder
2223 * loescht obsolete Props. Repariert bestimmte Datenstrukturen im Spieler.
2224 * Wird von start_player() nach Laden des Savefiles gerufen.
2225 * Momentan wird z.B. die Groesse gesetzt, falls sie bisher 0 ist und die
2226 * Skills des Spielers initialisiert bzw. repariert oder auf die aktuellste
2227 * Version des Skillsystems
2228 * Ruft ggf. InitSkills() und FixSkills().
2229 * @param[in] newflag Gibt an, ob es ein neuerstellter Spieler ist.
2230 * @sa start_player(), InitSkills(), FixSkills()
2231 */
2232private void updates_after_restore(int newflag) {
MG Mud User88f12472016-06-24 23:31:02 +02002233 // Seher duerfen die Fluchtrichtung uebermitteln lassen.
2234 // Eigentlich koennte es Merlin machen. Dummerweise gibt es ja auch alte
2235 // Seher und dann kann es gleiche fuer alle hier gemacht werden. (Ob der
2236 // Code jemals rauskann?)
2237 //TODO: Irgendwann alle Seher korrigieren und Code nach Merlin schieben...
2238 if (IS_SEER(ME))
2239 SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS) | CAN_REPORT_WIMPY_DIR);
2240
2241 // ggf. Invis-Eigenschaft aus dem Loginobjekt abrufen (Invislogin), koennte
2242 // ja anders als aus Savefile sein. Gesetztes P_INVIS aus diesem aber
2243 // beibehalten.
2244 if (IS_LEARNER(ME) && !QueryProp(P_INVIS)
2245// && load_name(previous_object()) == "/secure/login"
2246 )
2247 {
2248 SetProp(P_INVIS, previous_object()->query_invis());
2249 if (QueryProp(P_INVIS))
2250 tell_object(ME, "DU BIST UNSICHTBAR!\n" );
2251 }
2252 "*"::updates_after_restore(newflag);
2253
2254 attributes::UpdateAttributes();
2255
2256 int size=Query(P_SIZE);
2257 if (!size) size=RandomSize();
2258 while(size==QueryProp(P_AVERAGE_SIZE))
2259 size=RandomSize();
2260 Set(P_SIZE,size);
2261
Zesstraab834bb2020-01-21 13:11:45 +01002262 // Props werden nicht mehr genutzt. TODO: irgendwann entfernen, wenn
2263 // sinnvoll oder Savefiles extern bereinigt.
MG Mud User88f12472016-06-24 23:31:02 +02002264 Set(P_SECOND_LIST, SAVE, F_MODE_AD);
2265 Set(P_SECOND_LIST, 0, F_VALUE);
Zesstraab834bb2020-01-21 13:11:45 +01002266 Set("creation_date", SAVE|SECURED|PROTECTED, F_MODE_AD);
MG Mud User88f12472016-06-24 23:31:02 +02002267}
2268
2269
2270/** Setzt den HC-Modus.
2271 */
2272varargs nomask void set_hc_play(string str,int val)
2273{
2274 string str1;
2275
2276 str1 = explode( object_name(previous_object()), "#" )[0];
2277
2278 if ( str1 != "/secure/login" &&
2279 previous_object()!=this_object() &&
2280 extern_call() &&
2281 (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
2282 capitalize(geteuid(ME)) != str ||
2283 geteuid(ME) != geteuid(previous_object())) ){
2284 write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
2285 return;
2286 }
2287
2288 hc_play=val;
2289}
2290
2291/** gibt den HC-Modus zurueck.
2292 */
2293nomask int query_hc_play()
2294{
2295 return hc_play;
2296}
2297
2298/** Initialisiert und aktiviert das Spielerobjekt.
2299 * Kann nur von /secure/login oder /secure/master gerufen werden.
2300 * Startet Telnet Negotiation, laedt Savefile, setzt einige Props.
2301 * Ruft updates_after_restore(), um div. Daten zu aktualisieren/reparieren.
2302 * Ruft create() aus /std/player/potion.c.
2303 * Bei neuem Spieler wird der entsprechende Event ausgeloest die Attribute
2304 * auf die Startwerte gesetzt.
2305 * Prueft Zweitiemarkierungen.
2306 * Ruft InitPlayer().
2307 * @param[in] str Name des Charakters, der geladen werden soll.
2308 * @param[in] ip textuelle Repraesentation der IP-Adresse des Spielers.
2309 * @return 1 bei Erfolg, 0 sonst.
2310 * @todo Div. Reparaturen/Updates nach updates_after_restore() auslagern.
2311 */
2312varargs nomask int start_player( string str, string ip )
2313{
2314 mixed second;
2315 int newflag; /* could player be restored? */
2316 string str1;
2317
2318 call_out( "disconnect", 600 );
2319
2320 str1 = explode( object_name(previous_object()), "#" )[0];
2321
2322 if ( str1 != "/secure/login" &&
2323 str1 != "/secure/master" &&
2324 (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
2325 capitalize(geteuid(ME)) != str ||
2326 geteuid(ME) != geteuid(previous_object())) ){
2327 write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
2328 destruct(ME);
2329 return 0;
2330 }
2331
2332 /* try to restore player. If it doesn't exist, set the new flag */
Zesstraf3f22662017-01-30 15:54:29 +01002333 newflag = !restore_object( SAVEPATH + lower_case(str)[0..0] + "/"
MG Mud User88f12472016-06-24 23:31:02 +02002334 +lower_case(str) );
2335
2336 updates_after_restore(newflag);
2337
Zesstra9ab40222020-01-16 23:07:12 +01002338 if ( query_once_interactive(ME) )
MG Mud User88f12472016-06-24 23:31:02 +02002339 {
Zesstra9ab40222020-01-16 23:07:12 +01002340 // Erstmal - sofern vorhanden - den manuell konfigurierten Zeichensatz
2341 // einstellen. Im folgenden wird dann versucht, die TELOP CHARSET
2342 // auszuhandeln, die aendert das evtl. nochmal.
2343 set_manual_encoding();
MG Mud User88f12472016-06-24 23:31:02 +02002344 // Telnet-Negotiations durchfuehren, aber nur die grundlegenden aus
2345 // telnetneg. Alle anderen sollten erst spaeter, nach vollstaendiger
2346 // Initialisierung gemacht werden.
Zesstra9ab40222020-01-16 23:07:12 +01002347 telnetneg::startup_telnet_negs();
2348 modify_prompt();
2349 Set( P_LAST_LOGIN, time(), F_VALUE );
MG Mud User88f12472016-06-24 23:31:02 +02002350 }
2351
2352 Set( P_WANTS_TO_LEARN, 1 ); // 1 sollte der default sein !!!
2353 Set( P_WANTS_TO_LEARN, PROTECTED, F_MODE_AS );
2354 // Eingefuegt 18.11.99, kann nach einem Jahr wieder raus:
2355 Set( P_TESTPLAYER, PROTECTED, F_MODE_AS );
2356
2357 if ( IS_LEARNER(ME) )
2358 SetProp( P_CAN_FLAGS, QueryProp(P_CAN_FLAGS)|CAN_REMOTE );
2359
2360 Set( P_NAME, str );
2361 Set( P_NAME, SECURED, F_MODE_AS );
2362
2363 if ( !QueryProp(P_NEEDED_QP) )
2364 SetProp( P_NEEDED_QP, REQ_QP );
2365
2366 Set( P_NEEDED_QP, NOSETMETHOD, F_SET_METHOD );
2367 Set( P_NEEDED_QP, SAVE|SECURED, F_MODE_AS );
2368
2369 /* autosave the player after 500 heartbeats */
2370 time_to_save = age + 500;
2371 potion::create(); /* DO IT HERE AFTER THE RESTORE !! */
2372
2373 AddId( getuid() );
2374 SetProp( P_AC, 0 );
2375 SetProp( P_WEAPON, 0 );
2376
2377 /* Set some things which wont be set when all is OK */
2378 SetProp( P_MAX_HP, (QueryAttribute(A_CON) * 8 + 42 ));
2379 SetProp( P_MAX_SP, (QueryAttribute(A_INT) * 8 + 42 ));
2380
2381 catch( bb = "/secure/bbmaster"->query_bb() );
2382
2383 /* If this is a new character, we call the adventurers guild to get
2384 * our first title !
2385 */
2386 if ( newflag ) {
2387 if ( QueryGuest())
2388 SetProp( P_TITLE, "ueberkommt das "MUDNAME" ..." );
2389
2390 Set( P_LEVEL, -1 );
2391 SetProp( P_ATTRIBUTES, ([ A_STR:1, A_CON:1, A_INT:1, A_DEX:1 ]) );
2392 SetProp( P_HP, QueryProp(P_MAX_HP) );
2393
2394 // Event ausloesen
2395 EVENTD->TriggerEvent(EVT_LIB_PLAYER_CREATION, ([
2396 E_OBJECT: ME,
2397 E_PLNAME: getuid(ME) ]) );
2398 }
2399
2400 InitPlayer();
2401
2402 // Padreic 01.02.1999
Zesstra85576452017-01-30 15:43:21 +01002403 if ( !IS_LEARNER(ME) && (second = QueryProp(P_SECOND)) )
2404 {
2405 if ( stringp(second) && lower_case(second)[0..3] == "von " )
2406 {
MG Mud User88f12472016-06-24 23:31:02 +02002407 second = lower_case(second[4..]);
2408 SetProp( P_SECOND, second );
2409 }
2410
2411 if ( !stringp(second ) ||
Zesstra85576452017-01-30 15:43:21 +01002412 !master()->find_userinfo(second))
2413 {
2414 // Wenns nur an Gross-/Kleinschreibung liegt, wird automatisch
2415 // korrigiert.
MG Mud User88f12472016-06-24 23:31:02 +02002416 if ( stringp(second) &&
Zesstra85576452017-01-30 15:43:21 +01002417 master()->find_userinfo(lower_case(second)))
2418 {
MG Mud User88f12472016-06-24 23:31:02 +02002419 SetProp( P_SECOND, lower_case(second) );
2420 log_file( "WRONG_SECOND",
2421 sprintf( "%s: %s: P_SECOND = %O -> Automatisch "
2422 "korrigiert,\n",
2423 dtime(time()), object_name(), second ) );
2424 }
2425 else {
2426 tell_object( ME,
2427 "*\n*\n* Deine Zweitiemarkierung ist ungueltig, "
2428 "bitte aendere diese und sprich im\n* Zweifel "
2429 "bitte einen Erzmagier an.\n*\n*\n" );
2430
2431 log_file( "WRONG_SECOND",
2432 sprintf( "%s: %s: P_SECOND = %O\n",
2433 dtime(time()), object_name(), second ) );
2434 // ein bisschen deutlicher auffordern.. Padreic 08.04.1999
2435 move( "/d/gebirge/room/zwafflad", M_GO );
2436 }
2437 }
2438 }
2439 return(0);
2440}
2441
2442/** Letzte Phase der Spielerinitialisierung beim Laden des Charakters.
2443 * Ruft enable_commands(), aktiviert den Heartbeat und aktiviert die
2444 * Kommandos aus den geerbten command.c, put_and_get.c, team.c, soul.c,
2445 * guide.c, setzt den Living Name.
2446 * Registriert UseSpell() als Catchall-Kommando.
2447 * Laesst den Spieler ggf. einen Level per /std/gilde aufsteigen, ruft
2448 * call_notify_player_change(), loest Login-Event aus.
2449 * Gibt Willkommenstexte, News und neue Mails aus.
2450 * Findet den Startraum und bewegt den Spieler dorthin.
2451 * Ruft FinalSetup() (aus den Rassenshells).
2452 * Begrenzt Geldmenge im Spieler (wegen Bankzweities) nach Tragkraft und
2453 * erstattet Geld bei Reboot.
2454 * Ruft set_is_wizard().
2455 * Startet Clonen der Autoloader.
2456 * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
2457 */
2458private void InitPlayer4()
2459{
2460 int num, str, ski;
2461 string err, called_from_ip;
2462 mixed start_place;
2463 object mon;
2464
2465 enable_commands();
2466 set_heart_beat(1);
2467 command::initialize();
2468 add_put_and_get_commands();
2469 add_team_commands();
2470 add_soul_commands();
2471 add_guide_commands();
2472 add_action( "UseSpell", "", 1 );
2473 set_living_name( getuid() );
2474 while ( remove_call_out("disconnect") != -1 )
2475 ;
2476
2477 if ( QueryProp(P_LEVEL) == -1 )
2478 {
2479 catch( "/std/gilde"->try_player_advance(this_object()) ;publish );
2480 }
2481
MG Mud User88f12472016-06-24 23:31:02 +02002482 if ( interactive(this_object()) ) {
2483 cat( "/etc/NEWS" );
2484
2485 NewbieIntroMsg();
2486
2487 if ( QueryProp(P_INVIS) && !IS_WIZARD(ME) )
2488 SetProp( P_INVIS, 0 );
2489
2490 catch( num = "secure/mailer"->FingerMail(getuid());publish );
2491
2492 if ( num )
2493 write( "Du hast " + num + " neue" +
2494 (num == 1 ? "n Brief" : " Briefe")+" im Postamt liegen.\n" );
2495
2496 catch( RegisterChannels();publish );
2497
2498 if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
2499 query_ip_number(ME) != called_from_ip ){
2500 string tmp;
2501
2502 if ( stringp(tmp = query_ip_name(called_from_ip)) &&
2503 tmp != called_from_ip )
2504 tmp = " [" + tmp + "]";
2505 else
2506 tmp = "";
2507
2508 write( "Das letzte Mal kamst Du von " + called_from_ip
2509 + tmp + ".\n" );
2510 }
2511
2512 Set( P_CALLED_FROM_IP, query_ip_number(ME) );
2513 }
2514
2515 if ( !stringp(default_home) || default_home == "" )
2516 default_home = "/gilden/abenteurer";
2517
2518 if ( IS_SEER(ME) && !IS_LEARNER(ME) )
2519 catch( start_place = HAUSVERWALTER->FindeHaus(getuid(ME));publish );
2520 // wenn der Spieler noch ganz frisch ist und noch wenig Stufenpunkte
2521 // gekriegt hat und das Tutorial noch nicht gemacht hat, startet er im
2522 // Tutorial.
2523 else if (QueryProp(P_LEP) <= 120
2524 && QM->HasMiniQuest(this_object(),
2525 "/d/anfaenger/ennox/tutorial/npcs/verkaeufer") != 1)
2526 start_place = "/room/welcome/"+ getuid(this_object());
2527 else
2528 start_place = 0;
2529
2530 if ( !start_place )
2531 start_place = QueryProp(P_START_HOME);
2532
2533 if( objectp(start_place) || (stringp(start_place) && start_place != "" ) ){
2534 if ((err = catch(move( start_place, M_GO|M_SILENT|M_NO_SHOW );publish))
2535 || !environment() )
2536 err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
2537 }
2538 else
2539 err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
2540
2541 if ( err )
2542 catch(move( "/gilden/abenteurer", M_GO|M_SILENT|M_NO_SHOW );publish);
2543
2544 // Die Shell muss FinalSetup() nicht implementieren. Daher Callother
2545 catch( ME->FinalSetup();publish );
2546
MG Mud User88f12472016-06-24 23:31:02 +02002547 // erst jetzt GMCP freigeben und zu verhandeln.
2548 gmcp::startup_telnet_negs();
2549
2550 // Schonmal sichern, falls ein Bug (Evalcost...) dazwischen kommen sollte.
2551 autoload_rest = autoload;
2552
2553 // Um Geld-Xties mit illegal viel Geld zu vermeiden, kommt ein Check:
2554 if ( !IS_LEARNER(ME) && !Query(P_TESTPLAYER) &&
2555 mon = present( "\ngeld", ME ) ){
2556 // maximale Kraft, die der Spieler haette haben koennen
2557 str = QueryAttribute(A_STR) + 4;
2558 if ( Query(P_FROG) )
2559 str += 30;
2560 if ( str < 1 )
2561 str = QueryRealAttribute(A_STR) + 4;
2562 if ( str > 30 )
2563 str = 30;
2564
2565 // Trageskill beachten
2566 ski = to_int(UseSkill( SK_CARRY, ([SI_SKILLARG : str ]) ));
2567 if ( !intp(ski) )
2568 ski = 0;
2569
2570 // Wieviel konnte der Spieler insgesamt maximal tragen?
2571 num = 9200 + str * 800 + ski;
2572 if ( num < 3000 )
2573 num = 3000;
2574
2575 // Verdoppeln fuer einen guten Container und nochmal 20% draufschlagen
2576 // zur Sicherheit. Das Ganze danach *4, um die maximale Anzahl Muenzen
2577 // zu erhalten.
2578 num = (int) (num * 8.8);
2579
2580 // Geld, das zuviel ist, auf den Maximalwert kuerzen.
2581 // Zur Sicherheit wird mitgeloggt. Falls ein Spieler sich (zu recht)
2582 // beschwert, kann man immer noch wieder die Summe korrigieren ;-)
2583 if ( (str = mon->QueryProp(P_AMOUNT)) > num ){
2584 mon->SetProp( P_AMOUNT, num );
2585 log_file( "ZUVIEL_GELD", sprintf( "%s: %s hatte %d Muenzen bei "
2586 "sich. Korrigiert auf %d.\n ",
2587 ctime(time())[4..],
2588 capitalize(getuid(ME)),
2589 str, num ) );
2590 }
2591 }
2592 int entschaedigung = QueryProp(P_CARRIED_VALUE);
2593 if ( entschaedigung > 0 )
2594 {
2595 write( "Du findest " + entschaedigung +
2596 " Muenzen, die Du beim letzten Mal verloren hast.\n" );
2597
2598 if ( MayAddWeight( entschaedigung / 4 ) ){
2599 write( "Weil Du nicht mehr soviel tragen kannst, spendest Du den "+
2600 "Rest der Zentralbank.\n" );
2601
2602 num = (QueryProp(P_MAX_WEIGHT) - query_weight_contents()) * 4;
2603 entschaedigung = (num < 0) ? 0 : num;
2604 }
2605
2606 AddMoney( entschaedigung );
2607 SetProp(P_CARRIED_VALUE,0);
2608 }
2609
2610 if ( !QueryProp(P_INVIS) )
2611 say( capitalize(name(WER)) + " betritt diese Welt.\n" );
2612 else
2613 write( "DU BIST UNSICHTBAR!\n\n" );
2614#if __EFUN_DEFINED__(set_is_wizard)
2615 if ( IS_WIZARD(getuid(ME)) )
2616 set_is_wizard( ME, 1 );
2617 else
2618 set_is_wizard( ME, 0 );
2619#endif
Zesstra9ea7e9a2021-11-01 11:33:34 +01002620
2621 // Objekte informieren, Loginevent etc.
2622 if ( interactive(ME) )
2623 call_notify_player_change(CNP_FLAG_ENTER);
2624
MG Mud User88f12472016-06-24 23:31:02 +02002625 if ( query_once_interactive(ME) )
2626 ListAwaited();
2627
2628 // Autoloader werden ganz zum Schluss geclont, da das bis zum bitteren
2629 // (Evalcost-)Ende geschieht und danach u.U. keine Rechenzeit fuer
2630 // andere Aktionen mehr ueber ist
2631 load_auto_objects( autoload );
2632}
2633
2634/** Setzt die Spielerinitialisierung nach start_player() fort.
2635 * Prueft den Wert der vom Spieler getragenen Sachen (fuer Entschaedigung
2636 * nach Reboot).
2637 * Setzt div. Properties des "Spielerkoerpers", die eigentlich gespeichert
2638 * werden, nach einem Reboot zurueck.
2639 * Fragt ggf. nach eMail-Adresse und uebergibt per input_to an
2640 * InitPlayer2().
2641 * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
2642*/
2643private void InitPlayer()
2644{
2645 string mailaddr;
2646
2647 // wenn es einen Crash gab, sollen Spieler nicht noch extra bestraft werden
2648 if ( file_time( "/save/" + getuid()[0..0] + "/" + getuid() + ".o" )
2649 < last_reboot_time() ){
2650 SetProp( P_FOOD, 0 );
2651 SetProp( P_DRINK, 0 );
2652 SetProp( P_ALCOHOL, 0 );
2653 SetProp( P_BLIND, 0 );
2654 SetProp( P_DEAF, 0 );
2655 SetProp( P_POISON, 0 );
2656 SetProp( P_GHOST, 0 );
2657 SetProp( P_FROG, 0 );
2658 SetProp( P_HP, QueryProp(P_MAX_HP) );
2659 SetProp( P_SP, QueryProp(P_MAX_SP) );
2660 }
2661
2662 if ( QueryGuest() )
2663 Set( P_MAILADDR, "none" );
2664 else if ( !(mailaddr = Query(P_MAILADDR)) || mailaddr == "" ) {
2665 write(break_string(
2666 "Eine gueltige EMail-Adresse erleichtert es erheblich, Dir "
2667 "ein neues Passwort setzen zu lassen, falls Du einmal Dein "
2668 "Passwort vergisst.",78));
2669 input_to( "getmailaddr",INPUT_PROMPT,
2670 "Gib bitte Deine EMail-Adresse an: " );
2671 return;
2672 }
2673 InitPlayer2();
2674}
2675
2676/** liest eMail-Adresse vom Spieler ein und speichert sie.
2677 * Uebergibt anschliessend an InitPlayer2()
2678 * @param[in] maddr Spielereingabe der Emailadresse.
2679 * @sa InitPlayer2().
2680 */
2681static void getmailaddr( string maddr )
2682{
2683 maddr = check_email(maddr);
2684
2685 if ( !stringp(maddr)) {
2686 write("Deine Eingabe scheint keine gueltige EMail-Adresse gewesen "
2687 "zu sein.\n");
2688 input_to( "getmailaddr", INPUT_PROMPT,
2689 "Gib bitte Deine EMail-Adresse an: " );
2690 return;
2691 }
2692 Set( P_MAILADDR, maddr );
2693 InitPlayer2();
2694}
2695
2696
2697/** Prueft Geschlecht des Spielers und fragt ggf. beim Spieler nach.
2698 * Uebergibt an InitPlayer3() oder get_gender().
2699 * @sa InitPlayer3(), get_gender().
2700 */
2701private void InitPlayer2()
2702{
2703 if( member(({ MALE, FEMALE }), QueryProp(P_GENDER) ) == -1 ) {
2704 input_to( "getgender", INPUT_PROMPT,
2705 "Bist Du maennlich oder weiblich: ");
2706 return;
2707 }
2708
2709 InitPlayer3();
2710}
2711
2712/** Liest Spielerantwort auf die Frage nach dem Geschlecht des Chars ein.
2713 * Wird gerufen von input_to().
2714 * Uebergibt an InitPlayer3().
2715 * @sa InitPlayer3().
2716 */
2717static void getgender( string gender_string )
2718{
2719 gender_string = lower_case( gender_string );
2720
2721 if ( sizeof(gender_string)==1 && gender_string[0] == 'm' ){
2722 write( "Willkommen, mein Herr!\n" );
2723 SetProp( P_GENDER, MALE );
2724 }
2725 else if ( sizeof(gender_string)==1 && gender_string[0] == 'w' ){
2726 write( "Willkommen, gnae' Frau!\n" );
2727 SetProp( P_GENDER, FEMALE );
2728 }
2729 else {
2730 write( "Wie? Was? Verstehe ich nicht!\n" );
2731 input_to( "getgender", INPUT_PROMPT,
2732 "Bist Du maennlich oder weiblich? (tippe m oder w): ");
2733 return;
2734 }
2735
2736 InitPlayer3();
2737}
2738
2739
2740/** Prueft Terminaltyp des Spielers und fragt ggf. beim Spieler nach.
2741 * Uebergibt an InitPlayer4() oder gettty().
2742 * @sa InitPlayer4(), gettty().
2743 */
2744private void InitPlayer3()
2745{
2746 if ( !QueryProp(P_TTY) || QueryProp(P_TTY) == "none" )
2747 {
2748 write( "Waehle einen Terminaltyp (kann spaeter mit <stty> geaendert "
Zesstra224e78f2022-02-10 12:07:34 +01002749 "werden).\n");
2750 input_to( "gettty", INPUT_PROMPT, "vt100, ansi, dumb (Standard: ansi): " );
MG Mud User88f12472016-06-24 23:31:02 +02002751 return;
2752 }
2753 InitPlayer4();
2754}
2755
2756/** Liest Spielerantwort auf die Frage nach dem Terminaltyp ein.
2757 * Wird gerufen von input_to().
2758 * Uebergibt an InitPlayer4().
2759 * @sa InitPlayer4().
2760 */
2761static void gettty( string ttystr )
2762{
2763 if ( !ttystr || ttystr == "" )
Zesstrabb7a4b32022-02-10 14:20:31 +01002764 ttystr = "ansi";
MG Mud User88f12472016-06-24 23:31:02 +02002765
2766 ttystr = lower_case(ttystr);
2767
2768 if ( ttystr == "vt100" ){
2769 write( "Dies sollte " + ANSI_BOLD + "fett" + ANSI_NORMAL + " sein.\n" );
2770 SetProp( P_TTY, ttystr );
2771 }
2772 else
2773 if ( ttystr == "ansi" ){
2774 write( "Dies sollte " + ANSI_RED + "rot" + ANSI_NORMAL +
2775 " sein.\n" );
2776 SetProp( P_TTY, "ansi" );
2777 }
2778 else if ( ttystr == "dumb" ){
2779 write( "Ohje, oede! Besorg Dir ein besseres Terminal!\n" );
2780 SetProp( P_TTY, "dumb" );
2781 }
2782 else {
2783 write( "Dieser Terminaltyp wird nicht unterstuetzt. Nimm bitte "
2784 "einen aus:\nvt100, ansi or dumb (Standard ist dumb).\n" );
2785 input_to( "gettty", INPUT_PROMPT,
2786 "vt100, ansi or dumb (Standard ist dumb): ");
2787 return;
2788 }
2789
2790 InitPlayer4();
2791}
2792
2793
2794/** Liefert die UID des Charakters zurueck, also den Charakternamen.
2795 * @return UID des Objekts (Charaktername).
2796 */
2797nomask string query_real_name() {
2798 /* ACHTUNG !! DIES LFUN DARF NICHT ENTFERNT WERDEN !!! */
2799 /* Sie wird vom Gamedriver (zB bei F_ED) aufgerufen !! */
2800 /* Ich bin da zwar nicht so ueberzeugt von, dass der Driver die heutzutage
2801 * noch ruft, aber die halbe Mudlib ruft sie. ;-) (Zesstra, 27.4.08)
2802 */
2803 return getuid();
2804}
2805
2806/*
2807 * the wizard command review: show player moving messages
2808 */
2809int review() {
2810 string *msg;
2811 write(short());
2812 write("Deine Bewegungen werden wie folgt gemeldet:\n"+
2813 "mout: "+name(WER)+" "+(msg=explode(QueryProp(P_MSGOUT),"#"))[0]
2814 +" <Richtung>"+(sizeof(msg)>1 ? msg[1] : "")+".\n"+
2815 "min: "+name(WER)+" "+QueryProp(P_MSGIN)+".\n"+
2816 "mmout: "+name(WER)+" "+QueryProp(P_MMSGOUT)+".\n"+
2817 "mmin: "+name(WER)+" "+QueryProp(P_MMSGIN)+".\n"+
2818 (IS_LEARNER(ME) ?
2819 "cmsg: "+name(WER)+" "+QueryProp(P_CLONE_MSG)+".\n"+
2820 "dmsg: <Irgendetwas> "+QueryProp(P_DESTRUCT_MSG)+".\n"
2821 : "")+
2822 "Wenn Du jemanden angreifst, sieht das so aus:\n"+
2823 name(WER)+" greift Dich"+QueryProp(P_HANDS)[0]+" an.\n");
2824 return 1;
2825}
2826
2827/*
2828 * set player moving messages
2829 */
2830
2831static int setmin(string str)
2832{
2833 SetProp(P_MSGIN, _unparsed_args()||"kommt an");
2834 write("Ok.\n");
2835 return 1;
2836}
2837
2838static int setmout(string str)
2839{
2840 string *msg;
2841
2842 if(sizeof(msg=explode((_unparsed_args()||"geht"),"#"))>2)
2843 {
2844 write("Du darfst nur einmal '#' fuer die Richtung angeben.\n");
2845 return 1;
2846 }
2847 if(sizeof(msg)>1)
2848 {
2849 if (msg[0]!="" && msg[0][<1]==' ') msg[0]=msg[0][0..<2];
2850 SetProp(P_MSGOUT, msg[0]+"#"+msg[1]);
2851 }
2852 else
2853 SetProp(P_MSGOUT, _unparsed_args()||"geht");
2854 write("Ok.\n");
2855 return 1;
2856}
2857
2858static int setmmin(string str)
2859{
2860 SetProp(P_MMSGIN, _unparsed_args()||"erscheint");
2861 write("Ok.\n");
2862 return 1;
2863}
2864
2865static int setmmout(string str)
2866{
2867 SetProp(P_MMSGOUT, _unparsed_args()||"verschwindet");
2868 write("Ok.\n");
2869 return 1;
2870}
2871
2872static int setcmsg(string str)
2873{
2874 SetProp(P_CLONE_MSG, _unparsed_args()||"zaubert etwas aus "
2875 + QueryPossPronoun(MALE,WEM) + " Aermel hervor");
2876 write("Ok.\n");
2877 return 1;
2878}
2879
2880static int setdmsg(string str)
2881{
2882 SetProp(P_DESTRUCT_MSG, _unparsed_args()||"wird von " + name(WER,1)
2883 + " zerstaeubt");
2884 write("Ok.\n");
2885 return 1;
2886}
2887
2888static int set_title(string str)
2889{
2890 string bonus;
2891
2892 if(!IS_SEER(this_object()) && !FAO_HAS_TITLE_GIFT(this_object()) )
2893 {
2894 return 0;
2895 }
2896
2897 bonus = "";
2898 if (!(str=_unparsed_args()))
2899 str = "";
2900 else if( str[0..2]=="\\b," || str[0..2]=="\\b'" )
2901 {
2902 bonus = "\b"; // ein backspace fuer ein (hoch)komma ist ok! :-)
2903 str = str[2..];
2904 }
2905 if(str=="0") // damit der Gildentitel zum Vorschein kommen kann
2906 SetProp(P_TITLE, 0);
2907 else
2908 SetProp(P_TITLE, bonus+str);
2909 write("Ok.\n");
2910 return 1;
2911}
2912
2913static int extra_input(string str, string look)
2914{
2915 if (str=="**")
2916 {
2917 if (look=="")
2918 SetProp(P_EXTRA_LOOK,0);
2919 else
2920 SetProp(P_EXTRA_LOOK,look);
2921 write("Ok.\n");
2922 return 1;
2923 }
2924 input_to("extra_input",INPUT_PROMPT, "]" ,look+str+"\n");
2925 return 1;
2926}
2927
2928static int extralook(mixed str)
2929{
2930 if( str=="?" )
2931 {
2932 write( "Dein Extralook ist : "+QueryProp(P_EXTRA_LOOK) + "\n");
2933 return 1;
2934 }
2935 write("Bitte gib Deinen Extra-Look ein. Beenden mit **:\n");
2936 input_to("extra_input",INPUT_PROMPT,"]","");
2937 return 1;
2938}
2939
2940static void calculate_value()
2941{
Arathorn5513dfc2021-03-16 21:52:20 +01002942 int carried_value, value;
MG Mud User88f12472016-06-24 23:31:02 +02002943
2944 carried_value=0;
2945 foreach(object ob: deep_inventory(ME)) {
2946 if (!ob->QueryProp(P_AUTOLOADOBJ))
Zesstra04f613c2019-11-27 23:32:54 +01002947 carried_value+=((value=({int})ob->QueryProp(P_VALUE)) > 1000 ? 1000 : value);
MG Mud User88f12472016-06-24 23:31:02 +02002948 }
2949 SetProp(P_CARRIED_VALUE, carried_value);
2950}
2951
MG Mud User88f12472016-06-24 23:31:02 +02002952void save_me(mixed value_items)
2953{
2954 // Gaeste werden nicht gespeichert
2955 if( getuid()[0..3]=="gast" )
2956 return;
2957
2958 // Autoloader identifizieren und speichern
2959 autoload=([]);
2960 foreach(object ob: deep_inventory(ME)) {
Arathorn82f47272019-11-25 21:21:39 +01002961 mixed val = ob->QueryProp( P_AUTOLOADOBJ );
MG Mud User88f12472016-06-24 23:31:02 +02002962 if (val && clonep(ob))
2963 {
2964 string obname=load_name(ob);
2965 if (obname == GELD)
2966 autoload[obname] += val;
2967 else
2968 autoload += ([obname:val]);
2969 }
2970 }
2971 // An noch nicht geclonte Autoloader denken!
2972 autoload += autoload_rest;
2973
2974 // Im Bedarfsfall Wert des Inventory bestimmen
2975 if (value_items)
2976 calculate_value();
2977 else
2978 SetProp(P_CARRIED_VALUE, 0);
2979
2980 // Logout-Zeit speichern
2981 if(query_once_interactive(ME) && !QueryProp(P_INVIS))
2982 Set(P_LAST_LOGOUT,time());
2983
2984 // Funktion zur Bestimmung des Gildenrating aufrufen
2985 string gilde=GUILD_DIR+QueryProp(P_GUILD);
2986 if (find_object(gilde) || file_size(gilde+".c")>-1)
2987 catch(call_other(gilde,"GuildRating",this_object());publish);
2988
2989 // Speichern des Spielers
Zesstra3162c892017-01-30 15:55:22 +01002990 save_object(SAVEPATH+getuid()[0..0]+"/" + getuid());
MG Mud User88f12472016-06-24 23:31:02 +02002991}
2992
2993static varargs void log_autoload( string file, string reason, mixed data, string error )
2994{
2995 if (member(autoload_error,file)!=-1) return;
2996 log_file(SHELLLOG("NO_AUTO_FILE"),sprintf("%s: %s: %s\nreason: cannot %s file\ndata: %O\n%s\n",
2997 ctime()[4..15],capitalize(getuid()),file,reason,data,
2998 (error?"Fehlermeldung: "+error+"\n":"")));
2999 autoload_error+=({file});
3000}
3001
3002// tics, die fuer autoloader reichen sollten:
3003#define SAFE_FOR_AUTOLOADER __MAX_EVAL_COST__/4
3004
3005private void load_auto_object( string file, mixed data )
3006{
3007 object ob;
3008 string error;
3009
3010 if( get_eval_cost() < SAFE_FOR_AUTOLOADER ) return;
3011 m_delete( autoload_rest, file );
3012 autoload_error-=({file});
3013
3014 if ( file == "/obj/money" )
3015 file = "/items/money";
3016 if ( file == "/obj/seercard" )
3017 file = "/items/seercard";
3018
3019 ob = find_object(file);
3020
3021 if (!ob)
3022 {
3023 if (file_size(file+".c")<0&&
3024 file_size(implode(explode(file,"/")[0..<2],"/")+
3025 "/virtual_compiler.c")<0)
3026 {
3027 log_autoload(file,"find",data,0);
3028 return;
3029 }
3030 if (error = catch(load_object(file); publish))
3031 {
3032 log_autoload(file,"load",data,error);
3033 return;
3034 }
3035 }
3036 if ( error = catch(ob = clone_object(file); publish) )
3037 {
3038 log_autoload(file,"clone",data, error);
3039 return;
3040 }
3041
3042 if ( error = catch(ob->SetProp( P_AUTOLOADOBJ, data ); publish) )
3043 {
3044 log_autoload(file,"SetProp",data, error);
3045 ob->remove(1);
3046 if (ob) destruct(ob);
3047 return;
3048 }
3049
3050 if ( error = catch(ob->move( ME, M_NOCHECK );publish) ) {
3051 log_autoload(file,"move",data, error);
3052 ob->remove(1);
3053 if(ob) destruct(ob);
3054 return;
3055 }
3056}
3057
3058static void load_auto_objects( mapping map_ldfied )
3059{
3060 if ( (!mappingp(map_ldfied) || !sizeof(map_ldfied)) && !sizeof(autoload_rest) )
3061 return;
3062
3063 // Mit Netztoten Spielern rechnen manche Autoloader nicht. Also
3064 // das Clonen unterbrechen und in Reconnect() wieder anwerfen.
3065 if ( !interactive() )
3066 return;
3067
3068 // Kleiner Hack: autoload_rest ist eine globale Variable, die beim
3069 // Clonen der einzelnen Autoloader direkt veraendert wird.
3070 // So lange das Mapping noch Eintraege hat, muessen wir noch fehlende
3071 // Autoloader clonen.
3072 if ( !sizeof(autoload_rest) )
3073 autoload_rest = map_ldfied;
3074
3075 // Schon hier einen call_out() zum "Nach"clonen von noch nicht geclonten
3076 // Autoloadern starten, da spaeter u.U. keine Rechenzeit mehr dafuer da ist.
3077 while ( remove_call_out("load_auto_objects") != -1 )
3078 /* do nothing */;
3079
3080 // Mit Parameter '0' aufrufen, da das globale Mapping benutzt wird.
3081 // Verzoegerung 0 in rekursiven Callouts ist bloed, also 1s Delay
3082 call_out( "load_auto_objects", 2, 0 );
3083
3084 // Mit catch() gegen die Evalcost-Falle!
3085 // Mit Absicht das walk_mapping() aus der "alten" Version erhalten und
3086 // nicht durch eine (einfachere) Schleife inkl. get_eval_cost() ersetzt,
3087 // da eine Schleife gegenueber der walk_mapping()-Loesung den Aufbau
3088 // der previous_object()-Kette veraendern wuerde; darauf testen aber
3089 // manche Objekte.
3090 catch( walk_mapping( autoload_rest, #'load_auto_object/*'*/ ) );
3091}
3092
3093/*
3094 * al_to_title: Make the numeric alignment value into a string
3095 */
3096static string al_to_title(int a)
3097{
3098 if (a >= KILL_NEUTRAL_ALIGNMENT * 100)
3099 return "heilig";
3100 if (a > KILL_NEUTRAL_ALIGNMENT * 20)
3101 return "gut";
3102 if (a > KILL_NEUTRAL_ALIGNMENT * 4)
3103 return "nett";
3104 if (a > - KILL_NEUTRAL_ALIGNMENT * 4)
3105 return "neutral";
3106 if (a > - KILL_NEUTRAL_ALIGNMENT * 20)
3107 return "frech";
3108 if (a > - KILL_NEUTRAL_ALIGNMENT * 100)
3109 return "boese";
3110 return "satanisch";
3111}
3112
3113static int toggle_whimpy_dir(string str) {
3114 SetProp(P_WIMPY_DIRECTION,str=_unparsed_args()||str);
3115 if (str)
3116 printf("Ok, Fluchtrichtung %O.\n",str);
3117 else
3118 printf("Ok, bevorzugte Fluchtrichtung deaktiviert.\n");
3119 return 1;
3120}
3121
3122static int toggle_whimpy(string str)
3123{
3124 int i;
3125
3126 if(!str || str=="" || (sscanf(str,"%d",i)<0))
3127 {
3128 write("vorsicht <hp>, 0<=hp<"+QueryProp(P_MAX_HP)+"\n");
3129 return 1;
3130 }
3131 if(i>=QueryProp(P_MAX_HP) || i<0)
3132 {
3133 write("Der Wert ist nicht erlaubt.\n");
3134 return 1;
3135 }
3136 if(!i) write("Prinz Eisenherz-Modus.\n");
3137 else write("Vorsicht-Modus ("+i+")\n");
3138 SetProp(P_WIMPY,i);
3139 return 1;
3140}
3141
3142/** Bestimmt, ob das Spielerobjekt beschattet werden darf.
3143 * Beschatten ist nur fuer Objekte erlaubt, die in /std/player/shadows
3144 * abgelegt sind.
3145 * Ausnahme: Testspieler mit gesetztem P_ALLOWED_SHADOW
3146 * @param[in] obj Objekt, was beschatten moechte.
3147 * @return 0, wenn Beschatten erlaubt, 1 sonst.
3148 */
3149varargs nomask int query_prevent_shadow(object obj)
3150{
3151 string what, allowed_shadow;
MG Mud User88f12472016-06-24 23:31:02 +02003152
3153// if ( Query(P_TESTPLAYER) )
3154// return 0;
3155
3156 if (obj){
3157 what=object_name(obj);
3158 if (what && what != "" &&
Arathorn5513dfc2021-03-16 21:52:20 +01003159 sscanf(what,"/std/player/shadows/%s#%~d",what)==2)
MG Mud User88f12472016-06-24 23:31:02 +02003160 return 0;
3161
3162 // Einem Testspieler kann man P_ALLOWED_SHADOW auf einen zu testenden
3163 // Shadow setzen.
3164 if ( Query(P_TESTPLAYER) &&
3165 stringp(what) &&
3166 stringp(allowed_shadow=Query(P_ALLOWED_SHADOW)) &&
3167 strstr(what, allowed_shadow)==0)
3168 return 0;
3169 }
3170 return 1;
3171}
3172
3173static int uhrzeit()
3174{
3175 write(dtime(time()+QueryProp(P_TIMEZONE)*3600)+".\n");
3176 return 1;
3177}
3178
3179protected string SetDefaultHome(string str)
3180{
3181 return default_home=str;
3182}
3183
3184string QueryDefaultHome()
3185{
3186 return default_home;
3187}
3188
3189protected string SetDefaultPrayRoom(string str)
3190{
3191 if(hc_play>1)
3192 {
3193 default_pray_room="/room/nirvana";
3194 }
3195 else
3196 default_pray_room=str;
3197
3198 return default_pray_room;
3199}
3200
3201string QueryPrayRoom()
3202{
3203 if(hc_play>1)
3204 {
3205 return "/room/nirvana";
3206 }
3207 string room = QueryProp(P_PRAY_ROOM);
3208 if (stringp(room))
3209 return room;
3210 else if (default_pray_room)
3211 return default_pray_room;
3212 // hoffentlich ist das wenigstens gesetzt.
3213 return default_home;
3214}
3215
3216void _restart_beat()
3217{
3218 tell_object(ME,
3219 "Der GameDriver teilt Dir mit: Dein Herzschlag hat wieder eingesetzt.\n");
3220 set_heart_beat(1);
3221}
3222
3223static int weg(string str)
3224{
3225 if (!(str=_unparsed_args()))
3226 {
3227 printf("Du bist nicht%s als abwesend gekennzeichnet.\n",
3228 QueryProp(P_AWAY) ? " mehr" : "");
3229 SetProp(P_AWAY, 0);
3230 return 1;
3231 }
3232 write("Du bist jetzt als abwesend gekennzeichnet.\n");
3233 SetProp(P_AWAY, str);
3234 return 1;
3235}
3236
3237/* Ein Befehl zum anschauen der Wegmeldung anderer Spieler */
3238static int wegmeldung(string player)
3239{
3240
3241 object player_ob;
3242 string weg;
3243
3244 if ( !player || player=="" ||
3245 player==lowerstring(this_player()->QueryProp(P_NAME)))
3246 {
3247 weg=this_player()->QueryProp(P_AWAY);
3248 write ("Du bist "+(weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
3249 if (weg)
3250 write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
3251 return 1;
3252 }
3253
3254 // Welcher Spieler ist gemeint?
3255 player_ob=find_player(player);
3256
3257 // Spieler nicht da oder Invis und Anfrager is kein Magier
3258 if (!player_ob ||
3259 (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
3260 {
3261 write(capitalize(player)+" ist gerade nicht im Spiel.\n");
3262 return 1;
3263 }
3264
3265 weg=player_ob->QueryProp(P_AWAY);
3266
3267 // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
3268 write (player_ob->QueryProp(P_NAME)+" ist "+
3269 (weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
3270
3271 if (weg)
3272 write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
3273
3274 return 1;
3275}
3276
3277static string timediff(int time)
3278{
3279 string ret;
3280
3281 ret="";
3282 if(time>=86400) {
3283 ret+=time/86400+"d ";
3284 time%=86400;
3285 }
3286 if(time<36000) ret+="0";
3287 ret+=time/3600+":";
3288 time%=3600;
3289 if(time<600) ret+="0";
3290 ret+=time/60+":";
3291 time%=60;
3292 if(time<10) ret+="0";
3293 ret+=time+"";
3294 return ret;
3295}
3296
3297
3298/* Ein Befehl zum anschauen der Idlezeit anderer Spieler */
3299static int idlezeit(string player)
3300{
3301
3302 object player_ob;
3303 int idle;
3304
3305 if ( !player || player=="" ||
3306 player==lowerstring(this_player()->QueryProp(P_NAME)))
3307 {
3308 write ("Du bist selber natuerlich gerade nicht idle.\n");
3309 return 1;
3310 }
3311
3312 // Welcher Spieler ist gemeint?
3313 player_ob=find_player(player);
3314
3315 // Spieler nicht da oder Invis und Anfrager is kein Magier
3316 if (!player_ob ||
3317 (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
3318 {
3319 write(capitalize(player)+" ist gerade nicht im Spiel.\n");
3320 return 1;
3321 }
3322
3323 idle=query_idle(player_ob);
3324
3325 // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
3326 write (player_ob->QueryProp(P_NAME)+" ist "+
3327 (idle>=60?timediff(idle):"nicht")+" passiv.\n");
3328
3329 return 1;
3330}
3331
3332
3333/** Belebt einen netztoten Spieler wieder.
3334 * Reaktiviert Heartbeats, bewegt den Spieler zurueck an den Ort, der eim
3335 * Einschlafen zum Aufwachen ermittelt wurde (im einfachsten Fall der Raum,
3336 * wo man eingeschlafen ist).
3337 */
3338static void ndead_revive()
3339{
Bugfix7bf5d4d2022-09-28 11:11:00 +02003340 string fname;
3341 int ret;
MG Mud User88f12472016-06-24 23:31:02 +02003342
Bugfix7bf5d4d2022-09-28 11:11:00 +02003343 set_heart_beat(1);
3344 ndead_next_check = NETDEAD_CHECK_TIME;
3345 ndead_currently = 0;
3346 ndead_lasttime = 0;
MG Mud User88f12472016-06-24 23:31:02 +02003347
Bugfix7bf5d4d2022-09-28 11:11:00 +02003348 if ( !objectp(ndead_location) &&
3349 stringp(ndead_l_filename) && sizeof(ndead_l_filename))
3350 {
3351 if ( member( ndead_l_filename, '#' ) == -1 )
3352 {
3353 catch(load_object( ndead_l_filename); publish);
3354 ndead_location = find_object(ndead_l_filename);
MG Mud User88f12472016-06-24 23:31:02 +02003355 }
Bugfix7bf5d4d2022-09-28 11:11:00 +02003356 else
3357 {
3358 ndead_location = find_object(ndead_l_filename);
3359 if ( !ndead_location && env_ndead_info )
3360 {
3361 fname = explode( ndead_l_filename, "#" )[0];
3362 catch(ndead_location =
3363 (load_object(fname)->SetProp(P_NETDEAD_INFO, env_ndead_info));
3364 publish);
3365 if ( !objectp(ndead_location) )
3366 {
3367 catch(load_object( ndead_location);publish);
3368 ndead_location = find_object(ndead_location);
3369 }
3370 }
MG Mud User88f12472016-06-24 23:31:02 +02003371 }
Bugfix7bf5d4d2022-09-28 11:11:00 +02003372 }
MG Mud User88f12472016-06-24 23:31:02 +02003373
Bugfix215c0912022-09-28 11:20:51 +02003374 // Wenn immer noch kein ndead_location da oder die Bewegung dahin nicht
3375 // klappt, ist das naechste Fallback das default_home der Shell.
Bugfix7bf5d4d2022-09-28 11:11:00 +02003376 if ( !objectp(ndead_location)
3377 || catch(ret = move( ndead_location, M_GO|M_SILENT );publish)
Bugfix215c0912022-09-28 11:20:51 +02003378 || ret != MOVE_OK )
Bugfix7bf5d4d2022-09-28 11:11:00 +02003379 {
Bugfix215c0912022-09-28 11:20:51 +02003380 ret = 0;
3381 if(catch(ret = move(default_home, M_GO | M_SILENT); publish) ||
3382 ret != MOVE_OK)
3383 {
3384 // und wenn auch das nicht klappt, bleibt die Abenteurergilde, die
3385 // hoffentlich erreichbar ist. Wenn nicht, dann solls hart abbrechen...
3386 move("/gilden/abenteurer", M_GO | M_SILENT);
3387 }
Bugfix7bf5d4d2022-09-28 11:11:00 +02003388 ndead_location = environment();
3389 }
3390
Bugfix7bf5d4d2022-09-28 11:11:00 +02003391 ndead_l_filename = 0;
3392 env_ndead_info = 0;
MG Mud User88f12472016-06-24 23:31:02 +02003393}
3394
3395/** Bewegt einen netztoten Spieler in den Netztotenraum
3396 * Gerufen von heartbeat().
3397 * Zerstoert Gaeste, verlaesst ggf. das Team, ermittelt, ob der Spieler beim
3398 * Aufwachen in das alte Environment bewegt werden soll oder in einen anderen
3399 * Raum, hierzu wird im Environment P_NETDEAD_INFO abgefragt.
3400 * Deaktiviert die Kommandos per disable_commands().
3401 */
3402static void ndead_move_me() {
3403 object team;
MG Mud User88f12472016-06-24 23:31:02 +02003404
3405 set_heart_beat(0);
3406 stop_heart_beats();
3407 if (QueryGuest()) {
3408 quit();
3409 if (ME)
3410 remove();
3411 if (ME)
3412 destruct(ME);
3413 return;
3414 }
3415 ndead_next_check=NETDEAD_CHECK_TIME;
3416 ndead_currently=1;
3417 ndead_lasttime=0;
3418 ndead_location=environment();
3419 if (objectp(ndead_location))
3420 ndead_l_filename=object_name(ndead_location);
3421
3422 if (objectp(environment())
3423 && sizeof(explode(object_name(environment()),"#")) > 1)
3424 env_ndead_info=environment()->QueryProp(P_NETDEAD_INFO);
3425 else
3426 env_ndead_info=0;
3427
3428 if ( objectp(team=Query(P_TEAM)) )
3429 // Der Test auf assoziierte Teammitglieder (== FolgeNPCs)
3430 // verhindert, dass Spieler nach "schlafe ein" aus dem
3431 // Team ausgetragen werden. -- 29.01.2002 Tiamak
3432 // && !objectp(amem=Query(P_TEAM_ASSOC_MEMBERS))
3433 // && !(pointerp(amem) && sizeof(amem)))
3434 team->RemoveMember(ME);
3435
3436 disable_commands();
3437 move(NETDEAD_ROOM,M_GO|M_NO_ATTACK|M_NOCHECK,"ins Reich der Netztoten");
3438}
3439
3440/** Ist dieser Character ein Gast?
3441 * @return 1, wenn Gast, 0 sonst.
3442 */
3443int QueryGuest()
3444{
Arathorn5513dfc2021-03-16 21:52:20 +01003445 return sscanf(getuid(),"gast%~d");
MG Mud User88f12472016-06-24 23:31:02 +02003446}
3447
3448/** Spielerkommando 'schlafe ein'.
3449 * Ruft remove_interactive() bei Spielern, bei Magiern wird quit() gerufen,
3450 * um das Magierobjekt zu zerstoeren.
3451 * @sa quit()
3452 */
3453int disconnect(string str)
3454{
3455 string verb;
3456 string desc = break_string(
3457 "\"schlafe ein\" beendet Deine Verbindung mit "MUDNAME". Du behaeltst "
3458 "Deine Sachen.\nFalls "MUDNAME" jedoch abstuerzt oder neu gestartet "
3459 "wird, waehrend Du weg bist, verlierst Du die meisten allerdings "
3460 "(genauso, als wenn Du Deine Verbindung mit \"ende\" beendet haettest). "
3461 "In diesem Fall bekommst Du dann eine kleine Entschaedigung."
3462 ,78,0,BS_LEAVE_MY_LFS);
3463
3464 verb=query_verb();
3465 if (!verb)
3466 verb="AUTOCALL";
3467 if (verb[0..5]=="schlaf" && str!="ein")
3468 {
3469 notify_fail(desc);
3470 return 0;
3471 }
3472 if (IS_LEARNER(this_object()))
3473 return quit();
3474
3475 tell_object(this_object(), desc);
3476
3477 if (clonep(environment()) && !environment()->QueryProp(P_NETDEAD_INFO))
3478 tell_object(this_object(),break_string(
3479 "\nACHTUNG: Wenn Du hier laengere Zeit schlaefst, "
3480 "kommst Du vermutlich nicht an diesen Ort zurueck!",78));
3481
3482 say(capitalize(name(WER))+" hat gerade die Verbindung zu "MUDNAME" gekappt.\n");
3483 remove_interactive(ME);
3484 call_out(#'clear_tell_history,4);
3485 return 1;
3486}
3487
3488static int finger (string str)
3489{
3490 string ret;
3491 mixed xname;
3492
3493 if (!str || str==""
3494 || sizeof(explode(str," ")-({"-n","-p","-s","-v","-a"}))>1)
3495 {
3496 write("finger <spielername> oder finger <spielername@mudname>\n"+
3497 "Bitte nur den reinen Spielernamen verwenden, keine Namensvorsaetze oder Titel\n");
3498 return 1;
3499 }
3500 xname=explode(str,"@");
3501 if(sizeof(xname)==2)
3502 {
3503 if (xname[0]=="-n " || xname[0]=="-p " || xname[0]=="-s ") {
3504 write("finger <spielername>@<mudname> - der Spielername fehlt.\n");
3505 return 1;
3506 }
3507 if (ret=INETD->_send_udp(xname[1],([
3508 REQUEST: "finger",
3509 SENDER: getuid(ME),
3510 DATA: (explode(xname[0]," ")-({"-n","-p","-s"}))[0]
3511 ]), 1))
3512 write(ret);
3513 else
3514 write("Anfrage abgeschickt.\n");
3515 return 1;
3516 }
3517 "/p/daemon/finger"->finger_single(str,1);
3518 return 1;
3519}
3520
3521string lalign(string str, int wid)
3522{
3523 return (str+" "+
3524 " ")[0..wid-1];
3525}
3526
3527#define MUDS_BAR "\
3528-------------------------------------------------------------------------------"
3529
3530private void format(mixed mud, mixed hosts, string output)
3531{
3532 output += lalign(hosts[mud][HOST_NAME], 20) + " " +
3533 (hosts[mud][HOST_STATUS] ?
3534 hosts[mud][HOST_STATUS] > 0 ?
3535 "UP " + ctime(hosts[mud][HOST_STATUS])[4..15] :
3536 "DOWN " + ctime(-hosts[mud][HOST_STATUS])[4..15]
3537 : "UNKNOWN Never accessed.") + "\n";
3538}
3539
3540static int muds() {
3541 mapping hosts;
MG Mud User88f12472016-06-24 23:31:02 +02003542 mixed muds, output;
3543
3544 output = lalign("Mudname", 20) + " Status Last access";
3545 output += "\n" + MUDS_BAR[0..sizeof(output)] + "\n";
3546 muds = sort_array(m_indices(hosts = INETD->query("hosts")),#'>);
3547 map(muds, #'format, hosts, &output);
3548 More(output);
3549 return 1;
3550}
3551
3552// **** local property methods
3553static int _set_level(int i)
3554{
3555 if (!intp(i)) return -1;
3556 if (i<1) return -1;
3557 Set(P_LEVEL, i);
3558 GMCP_Char( ([P_LEVEL: i]) );
3559 return i;
3560}
3561
3562static int _set_invis(int a)
3563{
3564 return Set(P_INVIS, intp(a) ? a : !Query(P_INVIS));
3565}
3566
3567/* sets the terminal type */
3568/* note: support vt100 (b/w), ansi (color), dumb (none) */
3569static string _set_tty(string str) {
3570 if(str != "dumb" && str != "vt100" && str != "ansi")
3571 return Query(P_TTY);
3572 return Set(P_TTY, str);
3573}
3574
3575static int stty(string str)
3576{
3577 if(str!="dumb"&&str!="vt100"&&str!="ansi"&&str!="reset")
3578 {
3579 write("Kommando: stty dumb|vt100|ansi oder reset\n");
3580 }
3581 if(str == "reset") {
3582 printf("Dieser Text sollte lesbar sein!\n");
3583 return 1;
3584 }
3585
3586 write("TTY steht jetzt auf "+SetProp(P_TTY,str)+".\n");
3587 if(str == "ansi" || str == "vt100") {
3588 printf("Terminal Test:\n");
3589 printf("VT100: fett unterstrichen "+
3590 "blinkend invers\n");
3591 if(str == "ansi") {
3592 printf("ANSI Farben und VT100 Attribute:\n");
3593 foreach(int fg: 30 .. 37) {
3594 foreach(int bg: 40 .. 47) {
3595 printf("[%d;%dm@", fg, bg);
3596 printf("[%d;%dm@", fg, bg);
3597 printf("[%d;%dm@", fg, bg);
3598 printf("[%d;%dm@", fg, bg);
3599 }
3600 printf("\n");
3601 }
Zesstra37125992019-08-08 21:10:00 +02003602 printf("Sollte dieser Text hier nicht richtig lesbar\nsein, "+
3603 "benutze das Kommando 'stty reset'.\n");
MG Mud User88f12472016-06-24 23:31:02 +02003604 }
3605
3606 }
3607 return 1;
3608}
3609
3610int set_ascii_art(string str)
3611{
3612 if (str!="ein"&&str!="aus")
3613 {
3614 printf("Du moechtest 'Grafik' "+(QueryProp(P_NO_ASCII_ART)?"NICHT ":"")+
3615 "sehen.\n");
3616 }
3617
3618 if (str=="ein") {
3619 SetProp(P_NO_ASCII_ART, 0);
3620 printf("Zukuenftig moechtest Du 'Grafik' sehen.\n");
3621 }
3622
3623 if (str=="aus") {
3624 SetProp(P_NO_ASCII_ART, 1);
3625 printf("Zukuenftig moechtest Du KEINE 'Grafik' mehr sehen.\n");
3626 }
3627
3628
3629 return 1;
3630}
3631
3632int _set_shell_version(int arg)
3633{
3634 if (!intp(arg))
3635 return -1;
3636 Set(P_SHELL_VERSION,({QueryProp(P_RACE),arg}));
3637 return 1;
3638}
3639
3640int _query_shell_version()
3641{ mixed sv;
3642
3643 if (!(sv=Query(P_SHELL_VERSION)) || !pointerp(sv) || sizeof(sv)!=2 ||
3644 sv[0]!=QueryProp(P_RACE) || !intp(sv[1]))
3645 return 0;
3646 return sv[1];
3647}
3648
3649// XxXxXxXxXx
3650
3651int more(string str)
3652{
3653 if(!str)
3654 {
3655 notify_fail("Usage: more <file>\n");
3656 return 0;
3657 }
3658 if (file_size(str) <= 0) {
3659 notify_fail(str+": No such file\n");
3660 return 0;
3661 }
3662 More(str, 1);
3663 return 1;
3664}
3665
3666static int set_visualbell(string str)
3667{
3668 if(!str)
3669 {
3670 write("Derzeitige Einstellung fuer Tonausgabe: "+
3671 (QueryProp(P_VISUALBELL)?"AUS":"EIN")+".\n");
3672 return 1;
3673 }
3674 if (str=="ein")
3675 {
3676 if(!QueryProp(P_VISUALBELL))
3677 write("Die Tonausgabe stand schon auf EIN.\n");
3678 else
3679 {
3680 SetProp(P_VISUALBELL,0);
3681 write("OK, Tonausgabe auf EIN gestellt.\n");
3682 }
3683 }
3684 else
3685 if (str=="aus")
3686 {
3687 if(QueryProp(P_VISUALBELL))
3688 write("Die Tonausgabe stand schon auf AUS.\n");
3689 else
3690 {
3691 SetProp(P_VISUALBELL,1);
3692 write("OK, Tonausgabe auf AUS gestellt.\n");
3693 }
3694 }
3695 else
3696 write("Syntax: ton [ein|aus]\n");
3697 return 1;
3698}
3699
3700static int set_screensize(string str)
3701{
3702 int size;
3703
3704 if (str && (str[0..2] == "abs" || str[0..2]=="rel")) {
3705 size = QueryProp(P_MORE_FLAGS);
3706 if (str[0..2] == "abs") {
3707 size |= E_ABS;
3708 write("Es wird beim Prompt die Zeilenzahl des Textes angegeben.\n");
3709 }
3710 else {
3711 size &= ~E_ABS;
3712 write("Es wird beim Prompt der prozentuale Anteil des Textes angegeben.\n");
3713 }
3714 SetProp(P_MORE_FLAGS, size);
3715 return 1;
3716 }
3717
3718 if ( str && (str=="auto" || sscanf( str, "auto %d", size )) ){
3719 if ( size > 0 ){
3720 write("Ungueltiger Wert! "
3721 "In Verbindung mit 'auto' sind nur negative Werte erlaubt.\n");
3722 return 1;
3723 }
3724
3725 SetProp( P_SCREENSIZE, size-1 );
3726
3727 write("Ok, Deine Zeilenzahl wird nun automatisch ermittelt (derzeit "+
3728 QueryProp(P_SCREENSIZE)+").\n"+
3729 break_string("Bitte beachte, dass dies nur einwandfrei "
3730 "funktioniert, wenn Dein Client Telnetnegotiations "
3731 "unterstuetzt (siehe auch \"hilfe telnegs\").") );
3732 return 1;
3733 }
3734
3735 if ( !str || str=="" || !sscanf( str, "%d", size ) || size < 0 || size > 100){
3736 write(break_string(
3737 sprintf("Mit dem Befehl 'zeilen <groesse>' kannst Du einstellen, "
3738 "wieviele Zeilen bei mehrseitigen Texten auf einmal ausgegeben "
3739 "werden. Die angegebene Groesse muss zwischen 0 und 100 liegen. "
3740 "Bei Groesse 0 wird einfach alles ausgegeben (ohne Pause). Mit "
3741 "der Einstellung 'auto' wird die Groesse automatisch ueber "
3742 "die Telnetnegotiations ermittelt (siehe auch 'hilfe telnegs'). "
3743 "Um nach einer Seite Text noch etwas Platz zu haben, kann man z.B. "
3744 "'zeilen auto -3' einstellen.\n"
3745 "Die Voreinstellung ist 20 Zeilen.\n"
3746 "Mit 'zeilen abs[olut]' und 'zeilen rel[ativ]' kannst Du fest"
3747 "legen, ob im Prompt bei langen Texten die aktuelle Zeilennummer "
3748 "oder eine prozentuale Angabe ausgegeben wird.\n"
3749 "Deine aktuelle Einstellung ist %d%s Zeilen (%s).",
3750 QueryProp(P_SCREENSIZE),
3751 Query(P_SCREENSIZE) < 0 ? " 'automatische'" : "",
3752 QueryProp(P_MORE_FLAGS) & E_ABS ? "absolut" : "relativ"),78,0,1));
3753 return 1;
3754 }
3755
3756 SetProp( P_SCREENSIZE, size );
3757
3758 printf( "Okay, Deine Zeilenzahl steht nun auf %d.\n", size );
3759 return 1;
3760}
3761
3762static int _query_screensize()
3763{
3764 int sz,rows;
3765
3766 if ( (sz=Query(P_SCREENSIZE)) >= 0 )
3767 return sz;
3768
3769 if ( !rows=QueryProp(P_TTY_ROWS) )
3770 return 0;
3771
3772 return (rows+=sz) >= 5 ? rows : 5;
3773}
3774
3775static int presay(string str)
3776{
3777 if (!str=_unparsed_args())
3778 write("Dein Presay ist jetzt geloescht.\n");
3779 else
3780 printf("Dein Presay lautet jetzt: \"%s\".\n",str=capitalize(str));
3781 SetProp(P_PRESAY,str);
3782 return 1;
3783}
3784
3785static int sethands(string str)
3786{
3787 mixed *hands;
3788
3789 if (!(str=_unparsed_args()))
3790 {
3791 write("sethands <message>\n");
3792 return 1;
3793 }
3794 if (str=="0")
3795 hands=RaceDefault(P_HANDS);
3796 if (!hands || !pointerp(hands))
3797 hands=Query(P_HANDS);
3798 hands[0]=" "+str;
3799 Set(P_HANDS,hands);
3800 write("Ok.\n");
3801 return 1;
3802}
3803
3804static int inform(string str)
3805{
3806 switch (str) {
3807 case "on":
3808 case "ein":
3809 case "an":
3810 if (Query(P_INFORMME))
3811 write("Das hattest Du schon so eingestellt.\n");
3812 else
3813 {
3814 write("Kuenftig wirst Du informiert, wenn jemand das "MUDNAME" verlaesst/betritt.\n");
3815 Set(P_INFORMME,1);
3816 }
3817 return 1;
3818 case "aus":
3819 case "off":
3820 if (!Query(P_INFORMME))
3821 write("Das hattest Du schon so eingestellt.\n");
3822 else
3823 {
3824 write("Ok.\n");
3825 Set(P_INFORMME,0);
3826 }
3827 return 1;
3828 case 0:
3829 write("Inform-Mode ist "+(Query(P_INFORMME)?"an":"aus")+"geschaltet.\n");
3830 return 1;
3831 }
3832 write("inform an oder inform aus, bitte.\n");
3833 return 1;
3834}
3835
3836void delayed_write(mixed *what)
3837{
3838 if (!pointerp(what)||!sizeof(what)||!pointerp(what[0]))
3839 return;
3840 tell_object(ME,what[0][0]);
3841 if (sizeof(what)>1&&sizeof(what[0])>1)
3842 call_out("delayed_write",what[0][1],what[1..]);
3843}
3844
3845void notify_player_change(string who, int rein, int invis)
3846{
3847 string *list,name;
3848 mixed mlist;
3849
3850 if (invis) name="("+who+")";
3851 else name=who;
3852
3853 if (Query(P_INFORMME))
3854 {
3855 if (rein)
3856 tell_object(ME,name+" ist gerade ins "MUDNAME" gekommen.\n");
3857 else
3858 tell_object(ME,name+" hat gerade das "MUDNAME" verlassen.\n");
3859 }
3860
3861 if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
3862
3863 if(pointerp(list=Query(P_WAITFOR)) && sizeof(list) && member(list,who)!=-1)
3864 {
3865 if (!QueryProp(P_VISUALBELL))
3866 name+=sprintf("%c",7); // Char fuer Pieps an den String anhaengen.
3867 // Moechte der Spieler keine ASCII-Grafik sehen, wird diese Meldung ohne
3868 // Leerzeichen formatiert, so dass sie von Screenreadern vorgelesen wird.
3869 // Anderenfalls wuerde sie einzeln buchstabiert.
3870 if ( QueryProp(P_NO_ASCII_ART) )
3871 {
Bugfixe6503fb2025-02-21 18:44:47 +01003872 delayed_write( ({ ({ sprintf("%s IST JETZT %sDA!!!\n",
MG Mud User88f12472016-06-24 23:31:02 +02003873 name, (rein?"":"NICHT MEHR ")) }) }) );
3874 }
3875 else
3876 {
3877 delayed_write( ({ ({ sprintf("%s I S T J E T Z T %sD A !!!\n",
3878 name, (rein?"":"N I C H T M E H R ")) }) }) );
3879 }
3880 }
3881
3882 if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
3883 (mappingp(mlist)) && (mlist[who]))
3884 Show_WaitFor_Reason(who,invis);
3885}
3886
3887static int erwarte(string str)
3888{
3889 string *list,*str1;
3890 mixed mlist;
MG Mud User88f12472016-06-24 23:31:02 +02003891
3892 if (!mappingp(mlist=QueryProp(P_WAITFOR_REASON)))
3893 mlist=([]);
3894 if (!pointerp(list=Query(P_WAITFOR)))
3895 list=({});
3896
3897 if (!str || str=="-u")
3898 {
3899 if(Query(P_WAITFOR_FLAGS)&0x01)
3900 write("Du hast 'erwarte' temporaer deaktiviert.\n");
3901 write("Du erwartest jetzt");
3902 if (!sizeof(list))
3903 write(" niemanden mehr.\n");
3904 else
3905 {
3906 write(":\n");
3907 if (!str) list=sort_array(list,#'>);
3908 More(break_string(CountUp(list),78));
3909 }
3910 return 1;
3911 }
3912 if(str=="aus"){
3913 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)|0x01);
3914 write("Erwarte ist jetzt deaktiviert.\n");
3915 return 1;
3916 }
3917 if(str=="an" || str=="ein"){
3918 Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)&0xFE);
3919 write("Erwarte ist jetzt aktiv.\n");
3920 return 1;
3921 }
3922
3923 str1=explode(_unparsed_args()||""," ");
3924 if (sizeof(str1)==1)
3925 {
3926 if (str1[0]!="wegen")
3927 {
3928 str=capitalize(lower_case(str));
3929 if (member(list,str)!=-1)
3930 {
3931 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,str));
3932 list-=({str});
3933 write(str+" aus der Liste entfernt.\n");
3934 } else
3935 {
3936 if (sizeof(list)>1000)
3937 {
3938 write("Du erwartest schon genuegend!\n");
3939 return 1;
3940 }
3941 list+=({str});
3942 write(str+" an die Liste angehaengt.\n");
3943 }
3944 Set(P_WAITFOR,list);
3945 }
3946 else
3947 {
Zesstraf253faa2018-07-27 13:05:13 +02003948 if (sizeof(mlist))
MG Mud User88f12472016-06-24 23:31:02 +02003949 {
3950 write("Du erwartest aus einem bestimmten Grund:\n");
Zesstraf253faa2018-07-27 13:05:13 +02003951 write(break_string(CountUp(sort_array(m_indices(mlist),
3952 #'>))+".",78));
MG Mud User88f12472016-06-24 23:31:02 +02003953 }
3954 else write("Du erwartest niemanden aus einem bestimmten Grund.\n");
3955 }
3956 return 1;
3957 }
3958 notify_fail("Falsche Syntax, siehe 'hilfe erwarte'!\n");
3959 if (str1[1]!="wegen") return 0;
3960 if (sizeof(str1)==2)
3961 Show_WaitFor_Reason(capitalize(lower_case(str1[0])),0);
3962 else {
3963 string s=capitalize(lower_case(str1[0]));
3964 if (sizeof(str1)==3 && (str1[2]=="nichts" || str1[2]=="loeschen"))
3965 if (!mlist[s])
3966 write("Du hast "+s+" aus keinem bestimmten Grund erwartet!\n");
3967 else
3968 {
3969 SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,s));
3970 write("Du erwartest "+s+" aus keinem bestimmten Grund mehr!\n");
3971 }
3972 else
3973 {
Zesstra27649642018-07-27 12:59:25 +02003974 // Menge an erwarte-wegen Eintraegen begrenzen.
Zesstra5d9a0d72018-07-27 13:19:14 +02003975 int lim;
Zesstra27649642018-07-27 12:59:25 +02003976 if (IS_ARCH(ME)) lim=120;
3977 else if (IS_LEARNER(ME)) lim=80;
3978 else if (IS_SEER(ME)) lim=60;
3979 else lim=30;
3980 if (!mlist[s] && sizeof(mlist)>=lim)
MG Mud User88f12472016-06-24 23:31:02 +02003981 write("Sorry, aber Du erwartest schon genuegend Leute!\n");
3982 else
3983 {
Arathorn9b05bb42019-11-14 13:24:29 +01003984 // Meldung wieder zusammensetzen
3985 string meldung = implode(str1[2..], " ");
3986 // und Laenge auf 78 Zeichen abschneiden.
3987 meldung = sprintf("%.78s", meldung);
3988 m_add(mlist, s, meldung);
3989 SetProp(P_WAITFOR_REASON, mlist);
MG Mud User88f12472016-06-24 23:31:02 +02003990 Show_WaitFor_Reason(s,0);
3991 }
3992 }
3993 }
3994 return 1;
3995}
3996
3997static int uhrmeldung(string str)
3998{
3999 if (!(str=_unparsed_args()))
4000 {
4001 str=QueryProp(P_CLOCKMSG);
4002 if (!str)
4003 {
4004 write("Du hast die Standard-Uhrmeldung.\n");
4005 return 1;
4006 }
4007 if( !stringp(str) ) str = sprintf("%O\n",str);
4008 printf("Deine Uhrmeldung ist:\n%s\n",str[0..<2]);
4009 return 1;
4010 }
4011 if (str=="0")
4012 {
4013 SetProp(P_CLOCKMSG,0);
4014 write("Ok, Du hast jetzt wieder die Standard-Meldung.\n");
4015 return 1;
4016 }
4017 if (sizeof(explode(str,"%d"))>2)
4018 {
4019 write("Fehler, es darf nur ein %d in der Meldung vorkommen.\n");
4020 return 1;
4021 }
4022 /* Mehrere %-Parameter verursachen das Abschalten der Uhr zur vollen Stunde.
4023 */
4024 if (sizeof(explode(str,"%"))>2)
4025 {
4026 write("Fehler: Zuviele %-Parameter in der Meldung.\n");
4027 return 1;
4028 }
4029 /* Nur ein %-Parameter, aber der falsche: nicht sinnvoll. */
4030 else
4031 {
4032 int i = strstr(str,"%",0);
4033 if ( i>-1 && ( i==sizeof(str)-1 || str[i+1]!='d'))
4034 {
4035 write("Fehler: Falscher %-Parameter in der Meldung.\n");
4036 return 1;
4037 }
4038 }
4039 str+="\n";
4040 SetProp(P_CLOCKMSG,str);
4041 write("Ok.\n");
4042 return 1;
4043}
4044
4045static int zeitzone(string str)
4046{
4047 int zt;
4048 if(!str || str==""){
4049 if(!(zt=QueryProp(P_TIMEZONE)))
4050 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
4051 "eingestellt.\n");
4052 else if(zt>0)
4053 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
4054 "eingestellt.\n",zt);
4055 else
4056 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
4057 "Berlin eingestellt.\n",-zt);
4058 return 1;
4059 }
4060 if(sscanf(str,"utc %d",zt)==1) zt=(zt-1)%24;
4061 else zt=to_int(str)%24;
4062
4063 SetProp(P_TIMEZONE,zt);
4064
4065 if(!zt)
4066 write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
4067 "eingestellt.\n");
4068 else if(zt>0)
4069 printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
4070 "eingestellt.\n",zt);
4071 else
4072 printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
4073 "Berlin eingestellt.\n",-zt);
4074 return 1;
4075}
4076
4077static int emailanzeige(string str){
4078 notify_fail("Syntax: emailanzeige [alle|freunde|niemand]\n");
4079 if(!str || str==""){
4080 if(!(str=QueryProp(P_SHOWEMAIL)))str="Niemandem";
4081 else if(str=="alle")str="allen";
4082 else if(str=="freunde")str="Deinen Freunden";
4083 else if(str=="niemand")str="niemandem";
4084 else{
4085 SetProp(P_SHOWEMAIL,0);
4086 str="Niemandem";
4087 }
4088 write("Deine Email wird "+str+" angezeigt.\n");
4089 return 1;
4090 }
4091 else if(member(({"alle","freunde","niemand"}),str)==-1)return 0;
4092
4093 SetProp(P_SHOWEMAIL,str);
4094
4095 if(str=="alle")str="allen";
4096 else if(str=="freunde")str="Deinen Freunden";
4097 else str="niemandem";
4098 write("Deine Email wird "+str+" angezeigt.\n");
4099 return 1;
4100}
4101
4102static int zaubertraenke()
4103{
4104 More("/room/orakel"->TipListe());
4105 return 1;
4106}
4107
4108varargs static int angriffsmeldung(string arg) {
4109 if (arg=="ein" || arg=="an")
4110 SetProp(P_SHOW_ATTACK_MSG,1);
4111 else if (arg=="aus")
4112 SetProp(P_SHOW_ATTACK_MSG,0);
4113 if (QueryProp(P_SHOW_ATTACK_MSG))
4114 write("Du siehst saemtliche Angriffsmeldungen von Dir.\n");
4115 else
4116 write("Du siehst nur neue Angriffsmeldungen von Dir.\n");
4117 return 1;
4118}
4119
4120static mixed _query_localcmds()
4121{
4122 return ({({"zeilen","set_screensize",0,0}),
4123 ({"email","set_email",0,0}),
4124 ({"url","set_homepage",0,0}),
4125 ({"icq","set_icq",0,0}),
4126 ({"messenger", "set_messenger", 0, 0}),
4127 ({"ort","set_location",0,0}),
4128 ({"punkte","short_score",0,0}),
4129 ({"score","short_score",0,0}),
4130 ({"info","score",0,0}),
4131 ({"kurzinfo","very_short_score",0,0}),
4132 ({"quit","new_quit",0,0}),
4133 ({"ende","new_quit",0,0}),
4134 ({"disconnect","disconnect",0,0}),
4135 ({"schlaf","disconnect",1,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004136 ({"toete","kill",0,0}),
4137 ({"angriffsmeldung","angriffsmeldung",0,0}),
4138 ({"passw","change_password",1,0}),
4139 ({"hilfe","help",1,0}),
4140 ({"selbstloeschung","self_delete",0,0}),
4141 ({"spielpause","spielpause",0,0}),
4142 ({"spieldauer","spieldauer",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004143 ({"idee","ReportError",0,0}),
4144 ({"typo","ReportError",0,0}),
4145 ({"bug","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004146 ({"fehler","fehlerhilfe",0,0}),
Arathorn3437e392016-08-26 22:41:39 +02004147 ({"md","ReportError",0,0}),
4148 ({"detail","ReportError",0,0}),
Bugfixa75344d2017-06-16 14:04:48 +02004149 ({"syntaxhinweis","ReportError",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +02004150 ({"vorsicht","toggle_whimpy",0,0}),
4151 ({"stop","stop",0,0}),
4152 ({"kwho","kwho",0,0}),
4153 ({"kwer","kwho",0,0}),
4154 ({"kkwer","kkwho",0,0}),
4155 ({"kkwho","kkwho",0,0}),
4156 ({"who","who",0,0}),
4157 ({"wer","who",0,0}),
4158 ({"zeit","uhrzeit",0,0}),
4159 ({"uhrzeit","uhrzeit",0,0}),
4160 ({"weg","weg",0,0}),
4161 ({"wegmeldung", "wegmeldung", 0, 0}),
4162 ({"idlezeit", "idlezeit", 0, 0}),
4163 ({"finger","finger",0,0}),
4164 ({"muds","muds",0,0}),
4165 ({"emote","emote",0,0}),
4166 ({":","emote",1,0}),
4167 ({";","emote",1,0}),
4168 ({"remote","remote",0,SEER_LVL}),
4169 ({"r:","remote",1,0}),
4170 ({"r;","gremote",1,0}),
4171 ({"titel","set_title",0,0}),
4172 ({"review","review",0,SEER_LVL}),
4173 ({"setmin","setmin",0,SEER_LVL}),
4174 ({"setmout","setmout",0,SEER_LVL}),
4175 ({"setmmin","setmmin",0,SEER_LVL}),
4176 ({"setmmout","setmmout",0,SEER_LVL}),
4177 ({"sethands","sethands",0,SEER_LVL}),
4178 ({"presay","presay",0,SEER_LVL}),
4179 ({"extralook","extralook",0,SEER_LVL}),
4180 ({"fluchtrichtung","toggle_whimpy_dir",0,SEER_LVL}),
4181 ({"inform","inform",0,0}),
4182 ({"erwarte","erwarte",0,0}),
4183 ({"stty","stty",0,0}),
4184 ({"grafik", "set_ascii_art", 0, 0}),
4185 ({"uhrmeldung","uhrmeldung",0,0}),
4186 ({"zeitzone","zeitzone",0,0}),
4187 ({"behalte","behalte",0,0}),
4188 ({"zweitiemarkierung","zweitiemarkierung",0,0}),
4189 ({"emailanzeige","emailanzeige",0,0}),
4190 ({"topliste","topliste",0,0}),
4191 ({"ton","set_visualbell",0,0}),
4192 ({"telnegs","show_telnegs",0,0}),
4193 ({"spotte", "spotte", 0, 0}),
4194 ({"reise","reise",0,0}),
4195 ({"zaubertraenke","zaubertraenke",0,0}),
4196 ({"telnet","telnet_cmd",0,0}),
4197 })+
4198 command::_query_localcmds()+
4199 viewcmd::_query_localcmds()+
4200 comm::_query_localcmds()+
4201 skills::_query_localcmds()+
4202 description::_query_localcmds();
4203}
4204
4205static int _check_keep(object ob)
4206{
4207 return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
4208}
4209
4210static mixed _set_testplayer(mixed arg) {
4211 mixed res;
4212 object setob;
4213
4214 setob=this_player();
4215 if (!objectp(setob) || !query_once_interactive(setob))
4216 setob=this_interactive();
4217 if (!objectp(setob))
4218 setob=previous_object();
4219 if (setob && !IS_DEPUTY(setob)) {
4220 arg=geteuid(setob);
4221 if (!arg || arg=="NOBODY")
4222 arg=getuid(setob);
4223 arg=capitalize(arg);
4224 }
4225 res=Set(P_TESTPLAYER,arg);
4226 Set(P_TESTPLAYER,PROTECTED,F_MODE_AS);
4227 return res;
4228}
4229
4230int zweitiemarkierung(string arg)
4231{
4232 if (!QueryProp(P_SECOND))
4233 return _notify_fail("Aber Du bist doch gar kein Zweiti.\n"),0;
4234 notify_fail("Syntax: zweitiemarkierung [unsichtbar|sichtbar|name]\n");
4235 if (!arg)
4236 return 0;
4237 switch (arg)
4238 {
4239 case "unsichtbar" :
4240 SetProp(P_SECOND_MARK,-1);
4241 write("Jetzt sieht kein Spieler mehr, dass Du ein Zweiti bist.\n");
4242 return 1;
4243 case "sichtbar" :
4244 SetProp(P_SECOND_MARK,0);
4245 write("Jetzt sieht kein Spieler mehr, wessen Zweiti Du bist.\n");
4246 return 1;
4247 case "name" :
4248 SetProp(P_SECOND_MARK,1);
4249 write("Jetzt koennen alle sehen, wessen Zweiti Du bist.\n");
4250 return 1;
4251 }
4252 return 0;
4253}
4254
4255int topliste(string arg)
4256{
4257 if (!arg)
4258 {
4259 printf("Du hast Dich fuer die Topliste %s.\n",
4260 (QueryProp(P_NO_TOPLIST) ? "gesperrt" : "freigegeben"));
4261 return 1;
4262 }
4263 else if (member(({"j","ja","n","nein"}),arg)==-1)
4264 return _notify_fail("Syntax: topliste [ja|nein]\n"),0;
4265 if (arg[0]=='j')
4266 {
4267 SetProp(P_NO_TOPLIST,0);
4268 write("Du kannst jetzt (theoretisch) in der Topliste auftauchen.\n");
4269 }
4270 else
4271 {
4272 SetProp(P_NO_TOPLIST,1);
Zesstradd2d1982017-01-28 14:03:19 +01004273 "/secure/topliste"->DeletePlayer();
4274 write("Du wirst jetzt nicht (mehr) in den Toplisten auftauchen.\n");
MG Mud User88f12472016-06-24 23:31:02 +02004275 }
4276 Set(P_NO_TOPLIST,SAVE|PROTECTED,F_MODE_AS);
4277 return 1;
4278}
4279
4280int show_telnegs(string arg)
4281{
4282 if (!arg)
4283 {
4284 write("Du bekommst Aenderungen Deiner Fenstergroesse "+
4285 (QueryProp(P_TTY_SHOW)?"":"nicht ")+"angezeigt.\n");
4286 return 1;
4287 }
4288 if (member(({"ein","an","aus"}),arg)==-1)
4289 {
4290 write("Syntax: telnegs [ein|aus]\n");
4291 return 1;
4292 }
4293 if (arg=="ein" || arg=="an")
4294 {
4295 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"":"nun ")+
4296 "Aenderungen Deiner Fenstergroesse angezeigt.\n");
4297 Set(P_TTY_SHOW,1);
4298 return 1;
4299 }
4300 write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"nun ":"")+
4301 "Aenderungen Deiner Fenstergroesse nicht "+
4302 (QueryProp(P_TTY_SHOW)?"mehr ":"")+"angezeigt.\n");
4303 Set(P_TTY_SHOW,0);
4304 return 1;
4305}
4306
4307private int set_keep_alive(string str) {
4308 if (str == "ein") {
Zesstra268e3fd2019-07-25 14:29:01 +02004309 telnet_tm_counter = QueryProp(P_TELNET_KEEPALIVE_DELAY) || (240 / __HEART_BEAT_INTERVAL__);
4310 tell_object(this_object(), break_string( sprintf(
4311 "An Deinen Client werden jetzt alle %i Sekunden unsichtbare Daten "
MG Mud User88f12472016-06-24 23:31:02 +02004312 "geschickt, um zu verhindern, dass Deine Verbindung zum "MUDNAME
Zesstra268e3fd2019-07-25 14:29:01 +02004313 " beendet wird.",
4314 telnet_tm_counter*__HEART_BEAT_INTERVAL__), 78));
4315 // Bei Magiern ist der HB evtl. ausgeschaltet und muss eingeschaltet
4316 // werden.
4317 if (!object_info(this_object(), OC_HEART_BEAT))
4318 configure_object(this_object(), OC_HEART_BEAT, 1);
MG Mud User88f12472016-06-24 23:31:02 +02004319 }
4320 else if (str == "aus") {
4321 telnet_tm_counter = 0;
4322 tell_object(this_object(),break_string(
4323 "Du hast das Senden von unsichtbaren Daten (Keep-Alive-Pakete) an "
4324 "Deinen Client ausgeschaltet.",78));
4325 }
4326 else {
4327 if (!telnet_tm_counter)
4328 tell_object(this_object(), break_string(
4329 "An Deinen Client werden keine Keep-Alive-Pakete geschickt.",78));
4330 else
Bugfix5e832512022-10-26 17:13:07 +02004331 {
MG Mud User88f12472016-06-24 23:31:02 +02004332 tell_object(this_object(), break_string(
Bugfix5e832512022-10-26 17:13:07 +02004333 "An Deinen Client werden alle "
4334 + QueryProp(P_TELNET_KEEPALIVE_DELAY) * __HEART_BEAT_INTERVAL__
4335 + " Sekunden unsichtbare Daten geschickt, damit Deine Verbindung "
MG Mud User88f12472016-06-24 23:31:02 +02004336 "zum "MUDNAME" nicht beendet wird.",78));
Bugfix5e832512022-10-26 17:13:07 +02004337 }
MG Mud User88f12472016-06-24 23:31:02 +02004338 }
4339 return 1;
4340}
4341
4342private int print_telnet_rttime() {
4343 int rtt = QueryProp(P_TELNET_RTTIME);
4344 if (rtt>0)
4345 tell_object(ME, break_string(
4346 "Die letzte gemessene 'round-trip' Zeit vom MG zu Deinem Client "
4347 "und zurueck betrug " + rtt + " us.",78));
4348 else
4349 tell_object(ME, break_string(
4350 "Bislang wurde die 'round-trip' Zeit vom MG zu Deinem Client "
4351 "noch nicht gemessen oder Dein Client unterstuetzt dieses "
4352 "nicht.",78));
4353 return 1;
4354}
4355
Zesstra57cdbc32020-01-20 23:17:10 +01004356// Falls es eine per telnet vom Client ausgehandelte Einstellung fuer CHARSET
4357// gibt, hat die manuelle Einstellung von Spielern hier geringere Prioritaet
4358// und bildet nur den Fallback.
Zesstra9ab40222020-01-16 23:07:12 +01004359private int set_telnet_charset(string enc) {
Zesstra57cdbc32020-01-20 23:17:10 +01004360 struct telopt_s tdata = query_telnet_neg()[TELOPT_CHARSET];
Zesstra9ab40222020-01-16 23:07:12 +01004361 if (!sizeof(enc))
4362 {
Zesstra57cdbc32020-01-20 23:17:10 +01004363 if (!tdata->data || !tdata->data["accepted_charset"])
4364 {
4365 tell_object(ME, break_string(sprintf(
4366 "Zur Zeit ist der Zeichensatz \'%s\' aktiv. "
4367 "Alle Ausgaben an Dich werden in diesem Zeichensatz gesendet "
4368 "und wir erwarten alle Eingaben von Dir in diesem Zeichensatz. ",
4369 interactive_info(ME, IC_ENCODING)), 78));
4370 }
4371 else
4372 {
4373 tell_object(ME, break_string(sprintf(
Zesstra9ab40222020-01-16 23:07:12 +01004374 "Zur Zeit ist der Zeichensatz \'%s\' aktiv. "
4375 "Alle Ausgaben an Dich werden in diesem Zeichensatz gesendet "
4376 "und wir erwarten alle Eingaben von Dir in diesem Zeichensatz. "
Zesstra57cdbc32020-01-20 23:17:10 +01004377 "Dieser Zeichensatz wurde von Deinem Client ausgehandelt.",
4378 interactive_info(ME, IC_ENCODING)), 78));
4379 if (QueryProp(P_TELNET_CHARSET))
4380 tell_object(ME, break_string(sprintf(
4381 "Dein manuell eingestellter Zeichensatz ist \'%s\', welcher "
4382 "aber nur genutzt wird, wenn Dein Client keinen Zeichensatz "
4383 "aushandelt.", QueryProp(P_TELNET_CHARSET)),78));
4384
4385 }
Zesstra9ab40222020-01-16 23:07:12 +01004386 }
Zesstra57cdbc32020-01-20 23:17:10 +01004387 // Wenn es "loeschen" ist, wird die Prop genullt und wir stellen den Default
4388 // ein. Allerdings nur, wenn nix per telnet ausgehandelt wurde, dann wird
4389 // das beibehalten.
Zesstra9ab40222020-01-16 23:07:12 +01004390 else if (lower_case(enc) == "loeschen")
4391 {
4392 SetProp(P_TELNET_CHARSET, 0);
Zesstra57cdbc32020-01-20 23:17:10 +01004393 // wurde was per telnet option charset ausgehandelt? dann wird (weiterhin)
4394 // das genommen und nicht umgestellt.
4395 if (!tdata->data || !tdata->data["accepted_charset"])
4396 {
4397 configure_interactive(ME, IC_ENCODING, interactive_info(0,IC_ENCODING));
4398 tell_object(ME, break_string(sprintf(
Zesstra9ab40222020-01-16 23:07:12 +01004399 "Der Default \'%s\' wurde wieder hergestellt. "
4400 "Alle Ausgaben an Dich werden in diesem Zeichensatz gesendet "
4401 "und wir erwarten alle Eingaben von Dir in diesem Zeichensatz. "
4402 "Sollte Dein Client die Telnet-Option CHARSET unterstuetzen, kann "
Zesstra57cdbc32020-01-20 23:17:10 +01004403 "dieser allerdings direkt einen Zeichensatz aushandeln oder "
4404 "ausgehandelt haben, der dann stattdessen gilt.",
Zesstra9ab40222020-01-16 23:07:12 +01004405 interactive_info(ME, IC_ENCODING)), 78));
Zesstra57cdbc32020-01-20 23:17:10 +01004406 }
4407 else
4408 {
4409 tell_object(ME, break_string(sprintf(
4410 "Der Default \'%s\' wurde wieder hergestellt. Allerdings hat "
4411 "Dein Client mit dem MG den Zeichensatz \'%s\' ausgehandelt, "
4412 "welcher immer noch aktiv ist.",
4413 interactive_info(0, IC_ENCODING),
4414 interactive_info(ME, IC_ENCODING)), 78));
4415 }
Zesstra9ab40222020-01-16 23:07:12 +01004416 }
4417 else
4418 {
Zesstra57cdbc32020-01-20 23:17:10 +01004419 // Wenn der Zeichensatz keine //-Variante ist, machen wir den zu
Zesstra9ab40222020-01-16 23:07:12 +01004420 // einer. Das verhindert letztlich eine Menge Laufzeitfehler, wenn ein
4421 // Zeichen mal nicht darstellbar ist.
Zesstra57cdbc32020-01-20 23:17:10 +01004422 if (strstr(enc, "//") == -1)
Zesstra9ab40222020-01-16 23:07:12 +01004423 enc += "//TRANSLIT";
4424 if (catch(configure_interactive(ME, IC_ENCODING, enc); nolog))
4425 {
4426 tell_object(ME, break_string(sprintf(
4427 "Der Zeichensatz \'%s\' ist nicht gueltig oder zumindest auf "
4428 "diesem System nicht verwendbar.", enc),78));
4429 }
4430 else
4431 {
4432 SetProp(P_TELNET_CHARSET, interactive_info(ME, IC_ENCODING));
Zesstra57cdbc32020-01-20 23:17:10 +01004433 if (!tdata->data || !tdata->data["accepted_charset"])
4434 {
4435 tell_object(ME, break_string(sprintf(
Zesstra9ab40222020-01-16 23:07:12 +01004436 "Der Zeichensatz \'%s\' wurde eingestellt. Alle Ausgaben an "
4437 "Dich werden in diesem Zeichensatz gesendet und wir erwarten "
4438 "alle Eingaben von Dir in diesem Zeichensatz. Sollte Dein "
4439 "Client die Telnet-Option CHARSET unterstuetzen, kann "
4440 "dieser allerdings direkt einen Zeichensatz aushandeln, der "
4441 "dann stattdessen gilt.",
4442 interactive_info(ME, IC_ENCODING)),78));
Zesstra57cdbc32020-01-20 23:17:10 +01004443 }
4444 else
4445 {
4446 // Der via telnet ausgehandelte Charset muss wieder hergestellt
4447 // werden.
4448 configure_interactive(ME, IC_ENCODING,
4449 tdata->data["accepted_charset"]);
4450 tell_object(ME, break_string(sprintf(
4451 "Der Zeichensatz \'%s\' wurde gespeichert. Allerdings hat "
4452 "Dein Client mit dem MG den Zeichensatz \'%s\' ausgehandelt, "
4453 "welcher immer noch aktiv ist.",
4454 QueryProp(P_TELNET_CHARSET),
4455 interactive_info(ME, IC_ENCODING)), 78));
4456 }
Zesstra9ab40222020-01-16 23:07:12 +01004457 }
Zesstra57cdbc32020-01-20 23:17:10 +01004458
Zesstra9ab40222020-01-16 23:07:12 +01004459 }
4460 return 1;
4461}
4462
MG Mud User88f12472016-06-24 23:31:02 +02004463int telnet_cmd(string str) {
4464 if (!str) return 0;
4465 string *args = explode(str, " ");
4466 string newargs;
4467 if (sizeof(args) > 1)
4468 newargs = implode(args[1..], " ");
4469 else
4470 newargs = "";
4471
4472 switch(args[0])
4473 {
4474 case "keepalive":
4475 return set_keep_alive(newargs);
4476 case "rttime":
4477 return print_telnet_rttime();
Zesstra9ab40222020-01-16 23:07:12 +01004478 case "charset":
4479 return set_telnet_charset(newargs);
Zesstraab567652019-01-15 00:20:05 +01004480 case "tls":
Zesstra035bc7b2021-07-06 22:24:32 +02004481#if __EFUN_DEFINED__(tls_query_connection_state)
Zesstra363b1382019-01-15 00:36:24 +01004482 if (tls_query_connection_state(ME) > 0)
Zesstraab567652019-01-15 00:20:05 +01004483 tell_object(ME,
4484 "Deine Verbindung zum Morgengrauen ist TLS-verschluesselt.\n");
4485 else
Zesstra035bc7b2021-07-06 22:24:32 +02004486#endif
Zesstraab567652019-01-15 00:20:05 +01004487 tell_object(ME,
4488 "Deine Verbindung zum Morgengrauen ist nicht verschluesselt.\n");
4489 return 1;
Zesstrac7723982021-06-10 23:13:16 +02004490 case "client-gui":
Zesstra57e78832022-11-11 22:55:04 +01004491 case "gui":
Zesstrac7723982021-06-10 23:13:16 +02004492 GMCP_offer_clientgui(newargs);
4493 return 1;
MG Mud User88f12472016-06-24 23:31:02 +02004494 }
4495 return 0;
4496}
4497
4498int spotte( string str )
4499{
4500 _notify_fail( "Hier ist nichts, was Du verspotten koenntest!\n" );
4501 return 0;
4502}
4503
4504int behalte(string str)
4505{
4506 object ob,*obs;
4507 string s;
4508
4509 if (str)
4510 {
4511 if (str=="alles") {
4512 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, getuid());
4513 write("Ok!\n");
4514 return 1;
4515 }
4516 if (str=="nichts") {
4517 filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, 0);
4518 write("Ok!\n");
4519 return 1;
4520 }
Arathorndfbf8d22021-03-25 21:17:10 +01004521 if (!sizeof(obs=find_obs(str,PUT_GET_DROP)))
MG Mud User88f12472016-06-24 23:31:02 +02004522 {
4523 _notify_fail("Aber sowas hast Du nicht dabei!\n");
4524 return 0;
4525 }
4526 else ob=obs[0];
4527
4528 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4529 ob->SetProp(P_KEEP_ON_SELL,0);
4530 else
4531 ob->SetProp(P_KEEP_ON_SELL,geteuid(ME));
4532
4533 // erneut abfragen, da sich der Wert nicht geaendert haben muss
4534 if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
4535 write(break_string(sprintf("Ok, Du wirst %s jetzt bei 'verkaufe alles' "
4536 "behalten.\n",ob->name(WEN)),78));
4537 else
4538 write(break_string(sprintf("Ok, Du wirst %s beim naechsten 'verkaufe "
4539 "alles' mitverkaufen!\n",ob->name(WEN)),78));
4540
4541 return 1;
4542 }
4543 s=make_invlist(ME,filter(all_inventory(ME),#'_check_keep)); //'));
4544 More(s);
4545 return 1;
4546}
4547
4548static int _query_lep()
4549{
4550 int val;
4551 val = LEPMASTER->QueryLEP();
4552 Set( P_LEP, val );
4553 return val;
4554}
4555
4556static mixed _set_fraternitasdonoarchmagorum(mixed arg)
4557{
4558 if (!intp(arg)) return -1;
4559
4560 if ((!previous_object(1)||object_name(previous_object(1))!=FAO_MASTER) &&
4561 (!this_interactive() || !IS_ARCH(this_interactive())))
4562 return -1;
4563
4564 if (!intp(arg)) return -1;
4565
4566 log_file("fao/P_FAO",sprintf("%s - %s P_FAO gesetzt auf %O\n",
4567 dtime(time()),query_real_name(),arg) );
4568 return Set(P_FAO,arg);
4569}
4570
Zesstraca502032020-02-05 19:56:09 +01004571nomask public string set_realip(string str)
MG Mud User88f12472016-06-24 23:31:02 +02004572{
Zesstraca502032020-02-05 19:56:09 +01004573 if(previous_object()
4574 && strstr(object_name(previous_object()),"/secure")==0)
MG Mud User88f12472016-06-24 23:31:02 +02004575 {
4576 realip=str;
4577 }
Zesstraca502032020-02-05 19:56:09 +01004578 return realip;
MG Mud User88f12472016-06-24 23:31:02 +02004579}
4580
Zesstraca502032020-02-05 19:56:09 +01004581nomask public string query_realip()
MG Mud User88f12472016-06-24 23:31:02 +02004582{
Zesstraca502032020-02-05 19:56:09 +01004583 return realip ? realip : 0;
MG Mud User88f12472016-06-24 23:31:02 +02004584}
4585
4586mixed _query_netdead_env() {
4587 return ndead_location || ndead_l_filename;
4588}