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