blob: f91f1de304384f71d3ec2c7f2b536e48e8be7043 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// player/comm.c-- basic player communiction commands
4//
5// $Id: comm.c 9576 2016-06-18 15:00:01Z Zesstra $
6#pragma strong_types
7#pragma save_types
8#pragma no_clone
9#pragma pedantic
10//#pragma range_check
11
12inherit "/std/living/comm";
13inherit "/std/player/channel";
14inherit "/std/player/comm_structs";
15
16#include <input_to.h>
17
18#define NEED_PROTOTYPES
19#include <player/quest.h>
20#include <player/gmcp.h>
21#include <living/description.h>
22#undef NEED_PROTOTYPES
23
24#include <sys_debug.h>
25
26#include <thing/properties.h>
27#include <player/comm.h>
28#include <player/base.h>
29
30#include <properties.h>
31#include <config.h>
32#include <ansi.h>
33#include <wizlevels.h>
34#include <language.h>
35#include <udp.h>
36#include <defines.h>
37#include <daemon.h>
38#include <strings.h>
39#include <regexp.h>
40#include <interactive_info.h>
41
42#define TELLHIST_DISABLED 0
43#define TELLHIST_NO_MESSAGE 1
44#define TELLHIST_ENABLED 2
45#define TELLHIST_LONGLIFE 3
46
47#define ECHO_COST 50
48#define ERWIDER_PARAM ","
49
50#define ZDEBUG(x) if (find_player("zesstra"))\
51 efun::tell_object(find_player("zesstra"),"CommDBG: "+x+"\n")
52
53private int tell_history_enabled = TELLHIST_NO_MESSAGE;
54private nosave mapping tell_history=([]);
55private nosave string *commreceivers = ({});
56private nosave string last_comm_partner;
57private nosave int last_beep_time;
58
59// Statusreporte aktiviert? Binaere Flags (s. set_report())
60private int stat_reports;
61// interner Cache fuer die LP/KP/Gift-Werte fuer die Statusreport-Ausgaben
62// Eintraege (in dieser Reihenfolge): P_HP, P_SP, Giftstatus
63// Initialisierung erfolgt beim ersten Report nach Login
64private nosave mixed *report_cache;
65
66// Puffer fuer Kobold.
67private nosave struct msg_buffer_s kobold = (<msg_buffer_s>
68 buf: allocate(32),
69 index: -1,);
70#define MAX_KOBOLD_LIMIT 256
71
72varargs string name(int casus, int demonst);
73
74//local property prototypes
75static int _query_intermud();
76public int RemoveIgnore(string ign);
77public int AddIgnore(string ign);
78
79public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
80 string msg_prefix, object origin);
81
82// erzeugt sortierte Liste an Kommunikationspartnern
83private string *sorted_commpartners(int reversed);
84
85private nosave string *buffer = ({});
86
87void create()
88{
89 ::create();
90 Set(P_EARMUFFS, 0);
91 Set(P_EARMUFFS, SAVE, F_MODE);
92 Set(P_EARMUFFS, SECURED, F_MODE);
93 Set(P_INTERMUD, SAVE, F_MODE);
94 Set(P_IGNORE, ([]), F_VALUE);
95 Set(P_IGNORE, SAVE, F_MODE);
96 Set(P_BUFFER, SAVE, F_MODE);
97 Set(P_MESSAGE_PREPEND, SAVE, F_MODE_AS);
98 Set(P_MESSAGE_BEEP, SAVE, F_MODE_AS);
99}
100
101void create_super()
102{
103 set_next_reset(-1);
104}
105
106// uebermittelt eine MT_NOTIFICATION an this_object(), welche nicht ignoriert
107// werden kann und auch nicht gespeichert wird.
108protected void _notify(string msg, string action) {
109 ReceiveMsg(msg,
110 MT_NOTIFICATION|MSG_DONT_BUFFER|MSG_DONT_IGNORE|MSG_DONT_STORE,
111 action, 0, this_object());
112}
113
114protected void updates_after_restore(int newflag) {
115 // Altes Ignoriere loeschen...
116 mixed ign = Query(P_IGNORE,F_VALUE);
117 if (!mappingp(ign))
118 {
119 if (pointerp(ign))
120 _notify(break_string(
121 "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
122 "weil es eine Aktualisierung der Ignorierefunktion gab, "
123 "bei der eine Konversion der Daten leider nicht "
124 "moeglich war.",78), 0);
125
126 Set(P_IGNORE, ([]), F_VALUE);
127 }
128}
129
130static int set_report(string str) {
131 int canflags = QueryProp(P_CAN_FLAGS);
132
133 if(!str)
134 {
135 if (stat_reports) {
136 string *res=({});
137 if (stat_reports & DO_REPORT_HP)
138 res+=({"Lebenspunkte"});
139 if (stat_reports & DO_REPORT_SP)
140 res+=({"Konzentrationspunkte"});
141 if (stat_reports & DO_REPORT_POISON)
142 res+=({"Vergiftungen"});
143 if (stat_reports & DO_REPORT_WIMPY)
144 res+=({"Vorsicht"});
145
146 tell_object(ME,break_string(
147 "Dir werden jetzt Veraenderungen Deiner "
148 +CountUp(res) + " berichtet.",78));
149 }
150 else
151 tell_object(ME,
152 "Alle Statusreports sind ausgeschaltet.\n");
153
154 return 1;
155 }
156 else if (str == "aus") {
157 if (stat_reports & DO_REPORT_HP || stat_reports & DO_REPORT_WIMPY) {
158 string s="";
159 if (stat_reports & DO_REPORT_HP) {
160 str="ebenfalls ";
161 tell_object(ME, "Der Report wurde ausgeschaltet.\n");
162 }
163 if ( stat_reports & DO_REPORT_WIMPY ) {
164 tell_object(ME, "Der Vorsicht-Report wurde "+s+
165 "ausgeschaltet.\n");
166 }
167 stat_reports=0;
168 }
169 else {
170 tell_object(ME, "Der Report ist bereits ausgeschaltet.\n");
171 }
172 return 1;
173 }
174 else if (str == "ein") {
175 if ( stat_reports & DO_REPORT_HP ) {
176 tell_object(ME, "Der Report ist bereits eingeschaltet.\n");
177 return 1;
178 }
179 tell_object(ME, "Der Report wurde eingeschaltet.\n");
180 stat_reports |= DO_REPORT_HP;
181 if (!(canflags & CAN_REPORT_SP)) {
182 if (QueryQuest("Hilf den Gnarfen")==1) {
183 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_SP);
184 stat_reports |= DO_REPORT_SP;
185 }
186 else {
187 tell_object(ME, break_string(
188 "Fuer den Statusreport Deiner Konzentration musst Du jedoch "
189 "zunaechst die Quest \"Hilf den Gnarfen\" bestehen.",78));
190 }
191 }
192 else {
193 stat_reports |= DO_REPORT_SP;
194 }
195 if (!(canflags & CAN_REPORT_POISON)) {
196 if (QueryQuest("Katzenjammer")==1) {
197 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_POISON);
198 stat_reports |= DO_REPORT_POISON;
199 }
200 else {
201 tell_object(ME, break_string(
202 "Fuer den Statusreport Deiner Vergiftung musst Du jedoch "
203 "zunaechst die Quest \"Katzenjammer\" bestehen.",78));
204 }
205 }
206 else {
207 stat_reports |= DO_REPORT_POISON;
208 }
209 // Cache loeschen, damit beim naechsten Report-Event alle Daten neu
210 // eingetragen werden muessen. Muss beim Einschalten des Reports
211 // passieren, weil auch in der inaktiven Zeit weiterhin Aenderungen in
212 // status_report() eingehen, so dass der Cache zwar erst einmal leer ist,
213 // aber beim Wiedereinschalten nicht mehr ungueltig waere und somit
214 // veraltete Daten an den Spieler ausgegeben werden. Im unguenstigsten
215 // Fall wuerde das sogar dazu fuehren, dass die veralteten Daten lange
216 // Zeit nicht aktualisiert werden, wenn z.B. P_HP == P_MAX_HP, so dass
217 // kein P_HP-Event mehr eingeht.
218 report_cache=0;
219 }
220 else if (str == "vorsicht") {
221 if (!(canflags & CAN_REPORT_WIMPY)) {
222 if (QueryQuest("Schrat kann nicht einschlafen")==1) {
223 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_WIMPY);
224 tell_object(ME, "Der Vorsicht-Report wurde eingeschaltet.\n");
225 stat_reports |= DO_REPORT_WIMPY;
226 }
227 else {
228 tell_object(ME, break_string(
229 "Fuer den Statusreport Deiner Vorsicht musst Du "
230 "zunaechst die Quest \"Schrat kann nicht einschlafen\" "
231 "bestehen.",78));
232 }
233 }
234 else
235 {
236 stat_reports |= DO_REPORT_WIMPY;
237 }
238 // fuer Seher auch Bericht der Fluchtrichtung einschalten.
239 if ((stat_reports & DO_REPORT_WIMPY)
240 && !(stat_reports & DO_REPORT_WIMPY_DIR)
241 && ((canflags & CAN_REPORT_WIMPY) || IS_SEER(ME)))
242 {
243 stat_reports |= DO_REPORT_WIMPY_DIR;
244 }
245 }
246 // sendet einmalig genau jetzt den konfigurierten report. Kann zum testen
247 // (von Triggern) oder beim Login benutzt werden, wenn man einen initialen
248 // Datenbestand erhalten will.
249 else if (str=="senden")
250 {
251 // Es wird Ausgabe von LP und Vorsicht getriggert, das sendet beide
252 // Zeilen.
253 status_report(DO_REPORT_HP, QueryProp(P_HP));
254 status_report(DO_REPORT_WIMPY, QueryProp(P_WIMPY));
255 return 1;
256 }
257 else
258 return 0;
259 // nur aktuellen Zustand berichten
260 set_report(0);
261 return 1;
262}
263
264private string get_poison_desc(int p) {
265 string ret;
266 if ( intp(p) ) {
267 switch(p) {
268 case 0: ret="keins"; break;
269 case 1..3: ret="leicht"; break;
270 case 4..8: ret="gefaehrlich"; break;
271 default: ret="sehr ernst"; break;
272 }
273 return ret;
274 }
275 else return "(nicht verfuegbar)";
276}
277
278// sprintf()-Formatstrings fuer die Reportausgabe.
279#define REPORTLINE "LP: %3d, KP: %3s, Gift: %s.\n"
280#define REPORTLINE_WIMPY "Vorsicht: %d, Fluchtrichtung: %s.\n"
281// Defines zur Adressierung der Cache-Eintraege
282#define REP_HP 0
283#define REP_SP 1
284#define REP_POISON 2
285
286protected void status_report(int type, mixed val) {
287 // Wenn der Spieler GMCP hat und das sich um die Information kuemmert,
288 // erfolgt keine textuelle Ausgabe mehr. Daher return, wenn GMCP_Char()
289 // erfolg vermeldet hat.
290 int flags = QueryProp(P_CAN_FLAGS);
291 switch (type) {
292 case DO_REPORT_HP:
293 if (GMCP_Char( ([ P_HP: val ]) ) ) return;
294 break;
295 case DO_REPORT_SP:
296 if (!(flags & CAN_REPORT_SP)) return;
297 if (GMCP_Char( ([ P_SP: val ]) ) ) return;
298 break;
299 case DO_REPORT_POISON:
300 if (!(flags & CAN_REPORT_POISON)) return;
301 if (GMCP_Char( ([ P_POISON: val ]) ) ) return;
302 break;
303 case DO_REPORT_WIMPY:
304 if (!(flags & CAN_REPORT_WIMPY)) return;
305 if (GMCP_Char( ([ P_WIMPY: val ]) ) ) return;
306 break;
307 case DO_REPORT_WIMPY_DIR:
308 if (!(flags & CAN_REPORT_WIMPY_DIR)) return;
309 if (GMCP_Char( ([ P_WIMPY_DIRECTION: val ]) ) ) return;
310 break;
311 }
312
313 // konventionelle textuelle Ausgabe des Reports ab hier.
314 if (!(type & stat_reports))
315 return;
316
317 if ( !report_cache ) {
318 report_cache = ({
319 QueryProp(P_HP),
320 (stat_reports&DO_REPORT_SP) ? to_string(QueryProp(P_SP)) : "###",
321 (stat_reports&DO_REPORT_POISON) ?
322 get_poison_desc(QueryProp(P_POISON)) : "(nicht verfuegbar)"
323 });
324 }
325
326 switch(type) {
327 // LP berichten: Cache aktualisieren und Meldung ausgeben.
328 case DO_REPORT_HP:
329 report_cache[REP_HP]=val;
330 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
331 report_cache[REP_SP], report_cache[REP_POISON]));
332 break;
333 // KP berichten: Wenn der Spieler den Report freigeschaltet hat,
334 // wird bei Aenderungen gemeldet. Wenn nicht, aendert sich nur der
335 // Cache-Eintrag. So wird verhindert, dass ein Spieler ueber KP-
336 // Veraenderungen auch dann informiert wuerde, wenn er den KP-Report
337 // gar nicht benutzen koennte.
338 case DO_REPORT_SP:
339 report_cache[REP_SP]=to_string(val);
340 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
341 report_cache[REP_SP], report_cache[REP_POISON]));
342 break;
343 // Giftstatus berichten: Wenn der Giftreport freigeschaltet ist,
344 // Cache aktualisieren und berichten. Wenn nicht, aendert sich nur
345 // der Cache-Eintrag. Erlaeuterung hierzu s.o. beim KP-Report.
346 case DO_REPORT_POISON:
347 report_cache[REP_POISON] = get_poison_desc(val);
348 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
349 report_cache[REP_SP], report_cache[REP_POISON]));
350 break;
351 // Vorsicht-Report: kann ohne weitere Abfragen ausgegeben werden, da
352 // alle noetigen Checks schon zu Beginn dieser Funktion erledigt wurden.
353 // Lediglich der Inhalt der Meldung muss abhaengig vom Seherstatus
354 // konfiguriert werden.
355 case DO_REPORT_WIMPY:
356 string res;
357 if (IS_SEER(ME)) {
358 // QueryProp() aus Kostengruenden im if(), damit die Aufruf-
359 // Haeufigkeit zumindest ein wenig reduziert wird.
360 string dir = QueryProp(P_WIMPY_DIRECTION)||"keine";
361 res = sprintf(REPORTLINE_WIMPY, val, dir);
362 }
363 else
364 res = sprintf(REPORTLINE_WIMPY, val, "(nicht verfuegbar)");
365 tell_object(ME, res);
366 break;
367 // Fluchtrichtungs-Report: wird nur bei Sehern ausgegeben, damit
368 // nicht auch Spieler eine VS-/FR-Meldung bekommen, wenn z.B. eine
369 // externe Manipulation der Fluchtrichtung stattfindet, sie aber den
370 // Report mangels Seherstatus gar nicht freigeschaltet haben.
371 case DO_REPORT_WIMPY_DIR:
372 if (IS_SEER(ME)) {
373 if (!val) val = "keine";
374 tell_object(ME,sprintf(REPORTLINE_WIMPY, QueryProp(P_WIMPY), val));
375 }
376 break;
377 }
378}
379
380#undef REPORTLINE
381#undef REPORTLINE_WIMPY
382#undef REP_HP
383#undef REP_SP
384#undef REP_POISON
385
386private string permutate(string msg)
387{
388 // Kontrollzeichen rausfiltern. *seufz*
389 msg = regreplace(msg,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
390 object ob=QueryProp(P_PERM_STRING);
391 if (!objectp(ob))
392 return msg;
393
394 return (string)ob->permutate_string(msg)||"";
395}
396
397// neue nachricht an den Kobold anhaengen
398// Rueckgabewerte: MSG_BUFFER_FULL oder MSG_BUFFERED
399private int add_to_kobold(string msg, int msg_type, string msg_action,
400 string msg_prefix, object origin)
401{
402 // Nachricht soll im Kobold gespeichert werden.
403 // Kobold speichert Rohdaten und gibt spaeter das ganze auch wieder via
404 // ReceiveMsg() aus - dabei wird MSG_DONT_BUFFER | MSG_DONT_STORE gesetz,
405 // damit keine erneute Speicher in Kobold oder Komm-History erfolgt.
406
407 // wenn der Puffer zu klein ist, Groesse verdoppeln, wenn noch unterhalb
408 // des Limits.
409 if (kobold->index >= sizeof(kobold->buf)-1) {
410 if (sizeof(kobold->buf) < MAX_KOBOLD_LIMIT)
411 kobold->buf += allocate(sizeof(kobold->buf));
412 else
413 return MSG_BUFFER_FULL;
414 }
415 kobold->index = kobold->index +1;
416 // neue Nachricht an den Puffer anhaengen.
417 string sendername = query_once_interactive(origin) ?
418 origin->query_real_name() :
419 origin->name(WER) || "<Unbekannt>";
420 kobold->buf[kobold->index] = (<msg_s> msg: msg,
421 type : msg_type, action : msg_action, prefix : msg_prefix,
422 sendername : sendername);
423 return MSG_BUFFERED;
424}
425
426private void _flush_cache(int verbose) {
427 // nur mit genug Evalticks ausgeben.
428 if (get_eval_cost() < 100000) return;
429 if (kobold->index >= 0)
430 {
431 ReceiveMsg("Ein kleiner Kobold teilt Dir folgendes mit:",
432 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
433 0, 0, this_object());
434 int prepend = QueryProp(P_MESSAGE_PREPEND);
435 foreach(int i: 0 .. kobold->index) // '0 ..' ist wichtig!
436 {
437 struct msg_s msg = kobold->buf[i];
438 // dies ist dient der Fehlerabsicherung, falls es nen Fehler (z.B. TLE)
439 // in der Schleife unten gab: dann ist index nicht auf -1 gesetzt
440 // worden, aber einige Nachrichten sind schon geloescht.
441 if (!structp(msg)) continue;
442 // Ausgabe via efun::tell_object(), weil die Arbeit von ReceiveMsg()
443 // schon getan wurde. Allerdings muessen wir uns noch um den UMbruch
444 // kuemmern.
445 if ((msg->type) & MSG_DONT_WRAP)
446 msg->msg = (msg->prefix ? msg->prefix : "") + msg->msg;
447 else
448 {
449 int bsflags = msg->type & MSG_ALL_BS_FLAGS;
450 if (prepend)
451 bsflags |= BS_PREPEND_INDENT;
452 msg->msg = break_string(msg->msg, 78, msg->prefix, bsflags);
453 }
454 efun::tell_object(this_object(), msg->msg);
455 kobold->buf[i]=0;
456 }
457 kobold->index=-1;
458 }
459 else if (verbose)
460 {
461 ReceiveMsg("Der kleine Kobold hat leider nichts Neues fuer Dich.",
462 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
463 0, 0, this_object());
464 }
465}
466
467varargs int cmd_kobold(string arg)
468{
469 switch(arg)
470 {
471 case "ein":
472 SetProp(P_BUFFER, 1);
473 printf("Der Kobold merkt sich jetzt alles!\n"); break;
474 case "aus":
475 SetProp(P_BUFFER, 0);
476 printf("Der Kobold wird Dich nicht stoeren!\n"); break;
477 default: if(arg) printf("Der Kobold sagt: kobold ein oder kobold aus\n");
478 }
479 _flush_cache(1);
480 return 1;
481}
482
483public int TestIgnoreSimple(string *arg)
484{ string *ignore;
485
486 if (!pointerp(arg) || !mappingp(ignore=Query(P_IGNORE,F_VALUE)))
487 return 0;
488 foreach(string s: arg)
489 {
490 if (member(ignore,s))
491 return 1;
492 }
493 return 0;
494}
495
496//TODO: deprecated - entfernen, wenn Message() entfernt wird.
497private int check_ignore(mixed ignore, string verb, string name)
498{
499 if (ignore == verb)
500 return 1;
501 ignore = explode(ignore, ".");
502 return ((sizeof(ignore) > 1) &&
503 (name == ignore[0] && member(ignore[1..], verb) != -1));
504}
505
506private int comm_beep() {
507 if (QueryProp(P_VISUALBELL)) return 0; // kein ton
508 int beep_interval=(int)QueryProp(P_MESSAGE_BEEP);
509 if (!beep_interval || ((time()-last_beep_time) < beep_interval)) return 0;
510 last_beep_time=time();
511 return 1;
512}
513
514private varargs void add_to_tell_history( string uid, int sent, int recv,
515 string message, string indent, int flags )
516{
517 /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
518 Als Wert ist eine Strukur vom Typ chat_s eingetragen.
519 Strukturen chat_s und stored_msg_s sind in /std/player/comm_structs.c
520 definiert.
521 TODO fuer spaeter, gerade keine Zeit fuer:
522 Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
523 jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
524 weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
525 Element ist aeltestes Gespraech).
526 */
527
528 //TODO: Entfernen, wenn das nicht mehr passiert.
529 if (!stringp(uid))
530 {
531 ReceiveMsg(sprintf(
532 "\nadd_to_tell_history(): got bad uid argument %O."
533 "sent: %d, recv: %d, flags: %d, msg: %s",
534 uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);
535 }
536
537 // letzten Gespraechspartner fuer erwidere.
538 if (!(flags & MSGFLAG_REMOTE))
539 last_comm_partner = uid;
540
541 // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
542 // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
543 // Anfang.
544 if (sent) {
545 if (!sizeof(commreceivers))
546 commreceivers = ({uid});
547 else if (commreceivers[0] != uid) {
548 // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
549 // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
550 // entfernen.
551 // TODO: Effizienter gestalten.
552 commreceivers = ({uid}) + (commreceivers-({uid}));
553 // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
554 // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
555 // commreceivers drin sind.)
556 if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
557 commreceivers = commreceivers[0..MAX_SAVED_CHATS];
558 }
559 }
560
561 if (!tell_history_enabled)
562 return;
563
564 if (!indent && message[<1] == 10)
565 message = message[..<2];
566
567 struct chat_s chat;
568 // Gespraechspartner unbekannt?
569 if (!member(tell_history, uid)) {
570 // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
571 // dazu kommt.
572 if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
573 string deluid;
574 int zeit = __INT_MAX__;
575 foreach(string tuid, chat : tell_history) {
576 // aeltestes Gespraech suchen
577 if (zeit > chat->time_last_msg) {
578 deluid = tuid;
579 zeit = chat->time_last_msg;
580 }
581 }
582 // aeltestes Gespraech raus.
583 m_delete(tell_history, deluid);
584 if (member(commreceivers,deluid)>-1)
585 commreceivers-=({deluid});
586 }
587 // neues Gespraech anlegen
588 chat = (<chat_s> uid: uid, time_first_msg: time(),
589 time_last_msg: time(),
590 sentcount: sent, recvcount: recv,
591 msgbuf: 0, ptr: 0 );
592 tell_history[uid] = chat;
593 }
594 else {
595 // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
596 chat = tell_history[uid];
597 chat->time_last_msg = time();
598 chat->sentcount += sent;
599 chat->recvcount += recv;
600 }
601
602 if (tell_history_enabled < TELLHIST_ENABLED)
603 return;
604
605 // ggf. Array fuer Messages anlegen
606 if (!pointerp(chat->msgbuf))
607 chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
608
609 // Message-Struktur ermitteln oder neu anlegen
610 struct stored_msg_s msg;
611 if (!structp(chat->msgbuf[chat->ptr])) {
612 // neue Struct ins Array schreiben
613 chat->msgbuf[chat->ptr] = msg = (<stored_msg_s>);
614 }
615 else {
616 // alte Struct ueberschreiben
617 msg = chat->msgbuf[chat->ptr];
618 }
619 // Index auf naechste Messagestruktur ermitteln
620 chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
621 // Message speichern
622 msg->msg = message;
623 msg->prefix = indent;
624 msg->timestamp = time();
625}
626
627protected void clear_tell_history()
628{
629 /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
630 sofern der Spieler nichts abweichendes eingestellt hat. */
631
632#ifdef TELLHIST_LONGLIFE
633 if (tell_history_enabled == TELLHIST_LONGLIFE)
634 return;
635#endif
636
637 foreach (string uid, struct chat_s chat: tell_history)
638 if (pointerp(chat->msgbuf)) {
639 chat->msgbuf = 0;
640 chat->ptr = 0;
641 }
642}
643
644protected void reset(void)
645{
646 /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
647 Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
648 wie bei einem "schlafe ein" die Mitteilungen geloescht. */
649
650 if (!interactive())
651 clear_tell_history();
652
653}
654
655// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
656// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
657// werden soll.
658private void _send(object ob, string msg, int msg_type,
659 string msg_action, string msg_prefix)
660{
661 int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
662 switch(res) {
663 case MSG_DELIVERED:
664 break; // nix machen
665 case MSG_BUFFERED:
666 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden."
667 "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
668 "genommen. Er wird sie spaeter weiterleiten!",
669 MT_NOTIFICATION, msg_action, 0, this_object());
670 break;
671 case MSG_IGNORED:
672 case MSG_VERB_IGN:
673 case MSG_MUD_IGN:
674 ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
675 MT_NOTIFICATION, msg_action, 0, this_object());
676 break;
677 case MSG_SENSE_BLOCK:
678 ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
679 MT_NOTIFICATION, msg_action, 0, this_object());
680 break;
681 case MSG_BUFFER_FULL:
682 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden."
683 "Die Mitteilung ging verloren, denn der Kobold kann sich "
684 "nichts mehr merken!", MT_NOTIFICATION, msg_action,
685 0, this_object());
686 break;
687 default:
688 ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
689 "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
690 break;
691 }
692}
693
694// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
695// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
696// die Nachricht ggf. in den Kommhistory erfasst wird.
697// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
698protected varargs int _recv(object ob, string message, int flag, string indent)
699{
700 write(break_string(message, 78, indent,
701 QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
702 if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
703 query_once_interactive(ob))
704 {
705 if (flag & MSGFLAG_WHISPER)
706 add_to_tell_history(getuid(ob), 1, 0,
707 "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
708 flag);
709 else
710 add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
711 }
712 return 1;
713}
714
715// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
716// Fehlerbehandlung) ist es jedoch auch mal ein String.
717varargs int Message(string msg, int flag, string indent,
718 string cname, mixed sender)
719{
720 object ti;
721 string verb, reply, *ignore, tin;
722 int em, te;
723 mixed deaf;
724
725 // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
726 // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
727 // damit 'ignoriere name.debug' funktioniert.
728 if( flag == MSGFLAG_CHANNEL ){
729 if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
730 || msg[1..9]=="Warnungen"))
731 {
732 // Missbrauch der Variable 'ignore' als Zwischenspeicher
733 ignore = regexplode( msg, ":| |\\]" );
734 verb = lower_case(ignore[0][1..]);
735 tin = lower_case(ignore[2]);
736 }
737 else
738 {
739 if(cname)
740 verb=lower_case(cname);
741 else
742 verb=query_verb();
743 if( ti = this_interactive() )
744 {
745 tin = getuid(this_interactive());
746 }
747 else
748 {
749 //falls doch kein Objekt...
750 if (objectp(sender))
751 tin=lower_case(sender->name(RAW)||"<Unbekannt>");
752 }
753 }
754 }
755 else {
756 if( ti = this_interactive() )
757 tin = getuid(this_interactive());
758 verb = query_verb();
759 }
760
761 te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
762
763 // fuer "erwidere"
764 if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
765 if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
766 if (flag & MSGFLAG_WHISPER)
767 add_to_tell_history(getuid(ti), 0, 1,
768 capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
769 (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
770 ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
771 " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
772 else
773 add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
774 }
775 }
776 // Hoert der Spieler nicht?
777 em = (ti &&
778 (te || flag & MSGFLAG_SHOUT) &&
779 (QueryProp(P_EARMUFFS) &&
780 (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
781 ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
782
783 // Werden der Sender oder das Verb ignoriert?
784 if(!ti && tin && flag == MSGFLAG_CHANNEL)
785 {
786 if((member(ignore, tin) != -1))
787 {
788 return MESSAGE_IGNORE_YOU;
789 }
790 if(verb && sizeof(filter(ignore, #'check_ignore, verb, tin)) )
791 {
792 return MESSAGE_IGNORE_YOU;
793 }
794 }
795 if (ti && (member(ignore, getuid(ti)) != -1)) {
796 if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
797 efun::tell_object(ti, capitalize(name())+
798 " hoert gar nicht zu, was Du sagst.\n");
799 return MESSAGE_IGNORE_YOU;
800 }
801 if(tin && verb &&
802 sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
803 {
804 if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
805 verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
806 efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
807 return MESSAGE_IGNORE_VERB;
808 }
809 if (flag & MSGFLAG_RTELL) {
810 int at;
811
812 verb = lower_case(old_explode(msg, " ")[0][1..]);
813 at = member(verb, '@');
814 /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
815 add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
816
817 if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
818 return MESSAGE_IGNORE_YOU;
819 else if (at > 0 && member(ignore, verb[at..]) >= 0)
820 return MESSAGE_IGNORE_MUD;
821 }
822
823 // Taubheit/Oropax
824 te |= (flag & MSGFLAG_SAY);
825
826 if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
827 deaf = QueryProp(P_DEAF);
828 if (te)
829 reply = stringp(deaf) ?
830 capitalize(sprintf(deaf, name())) :
831 capitalize(name())+" ist momentan leider taub.\n";
832 }
833 else if (em)
834 reply = capitalize(name())+" hat Oropax in den Ohren.\n";
835
836 msg = break_string(msg, 78, indent,
837 (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
838
839 if(QueryProp(P_BUFFER) &&
840 (deaf ||
841 query_editing(this_object()) ||
842 query_input_pending(this_object())))
843 {
844 deaf = MESSAGE_DEAF;
845 if(flag & MSGFLAG_CACHE)
846 {
847 if(!stringp(reply))
848 reply = name()+" moechte gerade nicht gestoert werden.\n";
849
850 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
851
852 int res = add_to_kobold(msg, 0, 0, 0,
853 objectp(sender) ? sender : ME);
854 if(res == MSG_BUFFERED)
855 {
856
857 reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
858 "genommen.\nEr wird sie spaeter weiterleiten!";
859 deaf = MESSAGE_CACHE;
860 }
861 else {
862 reply += "Die Mitteilung ging verloren, denn "+
863 "der Kobold kann sich nichts mehr merken!";
864 deaf = MESSAGE_CACHE_FULL;
865 }
866 if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
867 efun::tell_object(ti, reply+"\n");
868 }
869 return deaf;
870 }
871 else if((deaf || em) &&
872 ( (flag & MSGFLAG_RTELL) ||
873 (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
874 if (te && ti)
875 efun::tell_object(ti, reply);
876 return MESSAGE_DEAF;
877 }
878
879 _flush_cache(0);
880 if(te && QueryProp(P_AWAY))
881 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
882
883 if (flag & (MSGFLAG_SAY | MSGFLAG_TELL) && comm_beep()) {
884 msg=MESSAGE_BEEP+msg;
885 }
886 efun::tell_object(ME, msg);
887 return MESSAGE_OK;
888}
889
890static int ignoriere(string str)
891{
892 str = _unparsed_args(1);
893 mapping ignore=Query(P_IGNORE, F_VALUE);
894
895 if (!str)
896 {
897 string* ignarr = m_indices(ignore);
898 if (!sizeof(ignarr))
899 tell_object(ME, "Du ignorierst niemanden.\n");
900 else
901 ReceiveMsg("Du ignorierst:\n"
902 + break_string(CountUp(map(sort_array(ignarr, #'> ),
903 #'capitalize )
904 ) + ".",78),
905 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
906 0,0,this_object());
907 return 1;
908 }
909 // trim spaces from args and convert to lower case.
910 str = lower_case(trim(str, TRIM_BOTH));
911
912 if (member(ignore, str))
913 {
914 RemoveIgnore(str);
915 tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
916 }
917 else if (sizeof(ignore)>100)
918 {
919 tell_object(ME, "Du ignorierst schon genuegend!\n");
920 }
921 else if (AddIgnore(str) == 1)
922 {
923 tell_object(ME,
924 sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
925 }
926 else
927 {
928 tell_object(ME,
929 sprintf("'%s' kannst Du nicht ignorieren.\n",str));
930 }
931 return 1;
932}
933
934
935static int _msg_beep(string str) {
936 int beep_interval;
937 notify_fail("Syntax: klingelton <1 bis 3600 Sekunden> oder klingelton aus\n");
938 if (stringp(str)) {
939 if (str=="aus")
940 SetProp(P_MESSAGE_BEEP,0);
941 else if ((beep_interval=to_int(str)) > 0 && beep_interval<=3600)
942 SetProp(P_MESSAGE_BEEP,beep_interval);
943 else return 0;
944 }
945
946 beep_interval=(int)QueryProp(P_MESSAGE_BEEP);
947 _notify("Ton bei Mitteilungen: "+
948 (beep_interval ? "aller "+beep_interval+" Sekunden." : "aus."),
949 query_verb());
950 return 1;
951}
952
953static int _msg_prepend(string str) {
954 int beep_interval;
955 notify_fail("Syntax: senderwiederholung ein/aus\n");
956 if (stringp(str)) {
957 if (str=="aus")
958 SetProp(P_MESSAGE_PREPEND,1);
959 else if (str=="ein")
960 SetProp(P_MESSAGE_PREPEND,0);
961 else return 0;
962 }
963
964 _notify("Senderwiederholung bei Mitteilungen: "+
965 ((int)QueryProp(P_MESSAGE_PREPEND) ? "aus" : "ein")+".",
966 query_verb());
967
968 return 1;
969}
970
971static int _communicate(mixed str, int silent)
972{
973 string verb;
974 string myname;
975 string msg;
976
977 if (!str || extern_call()) str=_unparsed_args()||"";
978 /* str=_unparsed_args()||""; */
979 verb = query_verb();
980 if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
981 if (str==""||str==" "||!str)
982 {
983 _notify("Was willst Du sagen?",MA_SAY);
984 return 1;
985 }
986 msg=permutate(str);
987
988 myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
989 !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
990 "":QueryProp(P_PRESAY))+name())||"";
991
992 // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
993 // ist, keine MT_COMM)
994 send_room(environment(), msg, MT_LISTEN, MA_SAY,
995 capitalize(myname)+" sagt: ", ({this_object()}) );
996
997 if(!silent)
998 {
999 ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1000 MA_SAY, "Du sagst: ", ME);
1001 }
1002 return 1;
1003}
1004
1005static int _shout_to_all(mixed str)
1006{
1007 string pre, myname, realname, wizards_msg, players_msg;
1008 string wizard_prefix, player_prefix;
1009 int chars;
1010
1011 if (!(str=_unparsed_args()))
1012 {
1013 _notify("Was willst Du rufen?",MA_SHOUT);
1014 return 1;
1015 }
1016 chars=sizeof(str)/2;
1017 if (chars<4) chars=4;
1018 pre = (!IS_LEARNER(ME) ||
1019 QueryProp(P_INVIS) ||
1020 !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
1021 realname = capitalize((pre + capitalize(getuid()))||"");
1022 myname = capitalize(pre + name()||"");
1023 if (QueryProp(P_INVIS))
1024 realname = "("+realname+")";
1025
1026 wizards_msg = permutate(str);
1027 wizard_prefix = myname+" ruft: ";
1028
1029 if(QueryProp(P_FROG)) {
1030 players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
1031 player_prefix = myname+" quakt: ";
1032 }
1033 else {
1034 players_msg = wizards_msg;
1035 player_prefix = wizard_prefix;
1036 }
1037
1038 if(!IS_LEARNER(this_player()))
1039 {
1040 if(QueryProp(P_GHOST)) {
1041 _notify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
1042 MA_SHOUT);
1043 return 1;
1044 }
1045 if (QueryProp(P_SP) <(chars+20))
1046 {
1047 _notify("Du musst erst wieder magische Kraefte sammeln.",
1048 MA_SHOUT);
1049 _notify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
1050 "Ebenen').", MA_SHOUT);
1051 return 1;
1052 }
1053 SetProp(P_SP, QueryProp(P_SP) - chars - 20);
1054 }
1055
1056 ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1057 "rufe", "Du rufst: ", ME);
1058
1059 foreach ( object ob : users()-({this_object()}) )
1060 if ( IS_LEARNER(ob) )
1061 ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR, MA_SHOUT, wizard_prefix,
1062 this_object());
1063 else
1064 ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR, MA_SHOUT, player_prefix,
1065 this_object());
1066
1067 return 1;
1068}
1069
1070varargs int _tell(string who, mixed msg)
1071{
1072 object ob;
1073 string away,myname,ret;
1074 mixed ignore,it;
1075 string *xname;
1076 int i,visflag;
1077
1078 if (extern_call() && this_interactive()!=ME) return 1;
1079 if (!who || !msg) {
1080 _notify("Was willst Du mitteilen?",MA_TELL);
1081 return 1;
1082 }
1083
1084 if(who == ERWIDER_PARAM)
1085 {
1086 if (!last_comm_partner)
1087 {
1088 _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
1089 "Du was erwidern\nkoenntest.\n");
1090 return 0;
1091 }
1092 who=last_comm_partner;
1093 }
1094
1095 // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
1096 if (who == ".")
1097 who = ".1";
1098
1099 if ( sscanf(who, ".%d", i) == 1 ) {
1100 if(i > 0 && i <= sizeof(commreceivers))
1101 who = commreceivers[i-1];
1102 else {
1103 _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
1104 return 0;
1105 }
1106 }
1107
1108 xname = explode(who, "@");
1109
1110 if (sizeof(xname) == 2)
1111 {
1112 if ( QueryProp(P_QP) )
1113 {
1114 if (ret=(string)INETD->_send_udp(xname[1],
1115 ([ REQUEST: "tell",
1116 RECIPIENT: xname[0],
1117 SENDER: getuid(ME),
1118 DATA: msg ]), 1))
1119 {
1120 _notify(ret, MA_TELL);
1121 }
1122 else
1123 {
1124 write("Nachricht abgeschickt.\n");
1125 add_to_tell_history(who, 1, 0, msg,
1126 "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_TELL, 1);
1127 }
1128 }
1129 else
1130 write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
1131 "Muds etwas mitteilen zu koennen.\n");
1132 return 1;
1133 }
1134
1135 if (!ob=find_player(it = lower_case(who)))
1136 {
1137 it = match_living(it, 0);
1138 if (!stringp(it))
1139 switch(it) {
1140 case -1:
1141 _notify("Das war nicht eindeutig!",MA_TELL);
1142 return 1;
1143 case -2:
1144 _notify("Kein solcher Spieler!",MA_TELL);
1145 return 1;
1146 }
1147 ob = find_player(it) || find_living(it);
1148 if(!ob) {
1149 _notify("Kein solcher Spieler!",MA_TELL);
1150 return 1;
1151 }
1152 }
1153
1154 if(QueryProp(P_INVIS)){
1155 if(!IS_LEARNER(ob))
1156 myname = name();
1157 else
1158 myname="("+
1159 ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
1160 capitalize(getuid()) + ")";
1161 }
1162 else
1163 myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
1164 QueryProp(P_PRESAY):"") + name();
1165 if (myname && sizeof(myname)) myname=capitalize(myname);
1166 // erstmal an Empfaenger senden
1167 _send(ob, permutate(msg), MT_COMM|MT_FAR, MA_TELL,
1168 myname + " teilt Dir mit: ");
1169
1170 // dann evtl. noch an Absender ausgeben...
1171 if (visflag = !ob->QueryProp(P_INVIS) || IS_LEARNER(this_player()))
1172 _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(it) + " mit: ");
1173 // oder irgendwas anderes an den Absender ausgeben...
1174 if (!visflag && interactive(ob))
1175 _notify("Kein solcher Spieler!",MA_TELL);
1176 else if (away = (string)ob->QueryProp(P_AWAY))
1177 ReceiveMsg( break_string( away, 78, capitalize(it)
1178 + " ist gerade nicht da: ", BS_INDENT_ONCE ),
1179 MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
1180 MA_TELL, 0, this_object());
1181 else if (interactive(ob) && (i=query_idle(ob))>=600)
1182 { //ab 10 Mins
1183 if (i<3600)
1184 away=time2string("%m %M",i);
1185 else
1186 away=time2string("%h %H und %m %M",i);
1187
1188 _notify(sprintf("%s ist seit %s voellig untaetig.",
1189 capitalize(it),away),
1190 MA_TELL);
1191 }
1192
1193 return 1;
1194}
1195
1196static int _teile(string str)
1197{
1198 string who, message;
1199 if (!(str=_unparsed_args())) return 0;
1200 if (sscanf(str, "%s mit %s", who, message) == 2)
1201 return _tell(who, message,1);
1202 return 0;
1203}
1204static int _teile_mit_alias(string str)
1205{
1206 str = _unparsed_args(), TRIM_LEFT;
1207 if (!str) return 0;
1208 str = trim(str, TRIM_LEFT);
1209 // Ziel muss min. 2 Buchstaben haben (.<nr>)
1210 if (sizeof(str) < 4) return 0;
1211 int pos = strstr(str, " ");
1212 if (pos >= 2)
1213 return _tell(str[..pos-1], str[pos+1..]);
1214 return 0;
1215}
1216
1217static int _erzaehle(string str)
1218{
1219 string who, message;
1220
1221 if (!(str=_unparsed_args())) return 0;
1222 if (sscanf(str, "%s %s", who, message) == 2)
1223 return _tell(who, message,1);
1224 return 0;
1225}
1226
1227static int _whisper(string str)
1228{
1229 object ob;
1230 string who;
1231 string msg;
1232 string myname;
1233
1234 if (!(str=_unparsed_args()) ||
1235 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1236 sscanf(str, "%s %s", who, msg) !=2 )) {
1237 _notify("Was willst Du wem zufluestern?",MA_SAY);
1238 return 1;
1239 }
1240 if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
1241 _notify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
1242 return 1;
1243 }
1244
1245 myname = capitalize((((IS_LEARNER(ME) &&
1246 !QueryProp(P_INVIS) &&
1247 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1248 QueryProp(P_PRESAY) : "") + name()) || "");
1249
1250 _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
1251 MSG_SAY, myname + " fluestert Dir zu: ");
1252 send_room(environment(),
1253 myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
1254 MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
1255
1256 _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
1257
1258
1259 return 1;
1260}
1261
1262static int _remote_whisper(string str)
1263{
1264 /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
1265 Nachricht nicht in der tell_history verewigt. */
1266
1267 object ob;
1268 string who, it;
1269 string msg;
1270 string myname;
1271
1272 if (!(str=_unparsed_args()) ||
1273 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1274 sscanf(str, "%s %s", who, msg) !=2 )) {
1275 _notify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
1276 return 1;
1277 }
1278
1279 if (!ob=find_player(it = lower_case(who)))
1280 {
1281 it = match_living(it, 0);
1282 if (!stringp(it))
1283 switch(it){
1284 case -1:
1285 _notify("Das war nicht eindeutig!",MA_EMOTE);
1286 return 1;
1287 case -2:
1288 _notify("Kein solcher Spieler!",MA_EMOTE);
1289 return 1;
1290 }
1291 ob = find_player(it);
1292 if(!ob) ob = find_living(it);
1293 if(!ob){
1294 _notify("Kein solcher Spieler!",MA_EMOTE);
1295 return 1;
1296 }
1297 }
1298 if (environment(ob) == environment()) {
1299 _notify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
1300 return 1;
1301 }
1302
1303 myname = capitalize((((IS_LEARNER(ME) &&
1304 !QueryProp(P_INVIS) &&
1305 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1306 QueryProp(P_PRESAY) : "") + name()) || "");
1307
1308 // An Empfaenger senden.
1309 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE, MA_EMOTE,
1310 myname + " fluestert Dir aus der Ferne zu: ");
1311
1312 // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
1313 if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
1314 _notify("Kein solcher Spieler!",MA_EMOTE);
1315 return 1;
1316 }
1317 // sonst eigene Meldung via _recv() ausgeben.
1318 else
1319 _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
1320 "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
1321
1322 return 1;
1323}
1324
1325static int _converse(string arg)
1326{
1327 _notify("Mit '**' wird das Gespraech beendet.",MA_SAY);
1328 if (stringp(arg) && strstr(arg, "-s") == 0)
1329 input_to("_converse_more", INPUT_PROMPT, "]", 1);
1330 else
1331 input_to("_converse_more", INPUT_PROMPT, "]", 0);
1332 return 1;
1333}
1334
1335static int _converse_more(mixed str, int silent)
1336{
1337 if (str == "**") {
1338 _notify("Ok.",MA_SAY);
1339 return 0;
1340 }
1341
1342 if(str != "")
1343 _communicate(str, silent);
1344
1345 input_to("_converse_more", INPUT_PROMPT, "]", silent);
1346 return 1;
1347}
1348
1349private int is_learner(object o) { return IS_LEARNER(o); }
1350
1351static int _shout_to_wizards(mixed str)
1352{
1353 int i, j;
1354 string myname;
1355 object *u;
1356
1357 str = _unparsed_args();
1358 if (!str||!sizeof(str)) {
1359 _notify("Was willst Du den Magiern zurufen?",MA_SHOUT);
1360 return 1;
1361 }
1362 // Kontrollzeichen rausfiltern.
1363 str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
1364 myname = capitalize(getuid(this_object()));
1365 if (!IS_LEARNER(this_object()))
1366 _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
1367
1368 // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
1369 filter(users(), #'is_learner)->ReceiveMsg(str,
1370 MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE,
1371 MA_SHOUT, myname+" an alle Magier: ", this_object());
1372
1373 return 1;
1374}
1375
1376static int _echo(string str) {
1377 if (!IS_SEER(ME) || (!IS_LEARNER(ME)
1378 && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
1379 return 0;
1380
1381 if (!(str=_unparsed_args())) {
1382 _notify("Was moechtest Du 'echoen'?", 0);
1383 return 1;
1384 }
1385
1386 if (!IS_LEARNER(this_interactive()))
1387 {
1388 if (QueryProp(P_GHOST))
1389 {
1390 _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
1391 return 0;
1392 }
1393 if (QueryProp(P_SP)<ECHO_COST)
1394 {
1395 _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
1396 return 0;
1397 }
1398 SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
1399 str=">\b"+str;
1400 log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
1401 str));
1402 }
1403 // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
1404 // Kommhistory noch im Kobold landen.
1405 send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
1406 MA_UNKNOWN, 0, 0);
1407 return 1;
1408}
1409
1410// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
1411// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
1412// und gefedert.
1413static string *_set_ignore(mixed arg)
1414{
1415 raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
1416 "Benutze AddIgnore/RemoveIgnore()!\n");
1417}
1418// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
1419// nix dran, dass alle TestIgnore() & Co benutzen sollen.
1420static string *_query_ignore() {
1421 mixed ign=Query(P_IGNORE, F_VALUE);
1422 if (mappingp(ign))
1423 return m_indices(ign);
1424 return ({});
1425}
1426
1427public int AddIgnore(string ign) {
1428 // Einige strings sind nicht erlaubt, z.B. konsekutive .
1429 if (!sizeof(ign)
1430 || regmatch(ign,"[.]{2,}",RE_PCRE)
1431 || regmatch(ign," ",RE_PCRE)
1432 || sizeof(explode(ign,"."))>3)
1433 return 0;
1434
1435 mapping ignores=Query(P_IGNORE, F_VALUE);
1436 ignores[ign]=time();
1437 // kein Set() noetig.
1438 return 1;
1439}
1440
1441public int RemoveIgnore(string ign)
1442{
1443 mapping ignores=Query(P_IGNORE,F_VALUE);
1444 m_delete(ignores,ign);
1445 // Kein Set() noetig
1446 return 1;
1447}
1448
1449static int _query_intermud()
1450{
1451 mixed tmp;
1452 return member(pointerp(tmp=Query(P_CHANNELS))?tmp:({}), "Intermud") > -1;
1453}
1454
1455
1456int erwidere(string str)
1457{
1458 str=_unparsed_args();
1459 if (!str) return 0;
1460 return _tell(ERWIDER_PARAM, str ,1);
1461}
1462
1463static int tmhist(string str)
1464{
1465
1466 if (str == "aus") {
1467 tell_history_enabled = TELLHIST_DISABLED;
1468 write("Ok, es wird nichts mehr gespeichert.\n");
1469 if (sizeof(tell_history)) {
1470 tell_history = ([]);
1471 commreceivers = ({});
1472 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1473 }
1474 return 1;
1475 }
1476
1477 if (str == "namen") {
1478 int flag;
1479 tell_history_enabled = TELLHIST_NO_MESSAGE;
1480 write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
1481 foreach (string uid, struct chat_s chat: tell_history)
1482 if (pointerp(chat->msgbuf)) {
1483 chat->msgbuf = 0;
1484 chat->ptr = 0;
1485 flag = 1;
1486 }
1487 if (flag)
1488 write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
1489 return 1;
1490 }
1491
1492 if (str == "ein" || str == "an") {
1493 tell_history_enabled = TELLHIST_ENABLED;
1494 write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
1495 return 1;
1496 }
1497
1498#ifdef TELLHIST_LONGLIFE
1499 if (str == "langlebig") {
1500 tell_history_enabled = TELLHIST_LONGLIFE;
1501 write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
1502 "Ende/Crash/\nReboot gespeichert.\n");
1503 return 1;
1504 }
1505#endif
1506
1507 if (str == "status") {
1508 switch (tell_history_enabled) {
1509 case TELLHIST_DISABLED:
1510 write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
1511 break;
1512 case TELLHIST_NO_MESSAGE:
1513 write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
1514 break;
1515 case TELLHIST_ENABLED:
1516 write("Deine Mitteilungen werden gespeichert.\n");
1517 break;
1518#ifdef TELLHIST_LONGLIFE
1519 case TELLHIST_LONGLIFE:
1520 write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
1521 "Crash/Reboot\ngespeichert.\n");
1522 break;
1523#endif
1524 }
1525 return 1;
1526 }
1527
1528 if (tell_history_enabled == TELLHIST_DISABLED) {
1529 _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
1530 return 0;
1531 }
1532
1533 if (!sizeof(tell_history)) {
1534 _notify_fail("Du hast noch keinem etwas mitgeteilt "
1535 "und noch keine Mitteilungen erhalten.\n");
1536 return 0;
1537 }
1538
1539 if (str && sizeof(str)) {
1540
1541 if (tell_history_enabled < TELLHIST_ENABLED) {
1542 _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
1543 return 0;
1544 }
1545
1546 string uid;
1547 if (member(tell_history, str)) {
1548 // Name gewuenscht, da der String in der History vorkommt.
1549 uid = str;
1550 }
1551 else {
1552 // evtl. ne Zahl angegeben.
1553 int i;
1554 string *partners = sorted_commpartners(0);
1555 if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
1556 uid = partners[i];
1557 else {
1558 notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
1559 return 0;
1560 }
1561 }
1562
1563 mixed *data = tell_history[uid]->msgbuf;
1564 if (!data) {
1565 _notify_fail(
1566 "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
1567 return 0;
1568 }
1569
1570 int ptr = tell_history[uid]->ptr;
1571
1572 More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
1573 data[0..ptr-1],
1574 function string (struct stored_msg_s msg) {
1575 if (!structp(msg)) return "";
1576 return break_string( msg->msg + " <"
1577 + strftime("%H:%M:%S",msg->timestamp) + ">", 78,
1578 msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
1579 } ) ) );
1580 return 1;
1581 }
1582
1583 string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
1584 int i;
1585 foreach (string uid : sorted_commpartners(0) ) {
1586 int j;
1587 struct chat_s chat = tell_history[uid];
1588 history += sprintf("%2d.%-4s %s %-11s %d gesendet/%d empfangen\n", ++i,
1589 ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
1590 strftime("%a, %e.%m.%y",chat->time_last_msg),
1591 capitalize(chat->uid), chat->sentcount, chat->recvcount);
1592 }
1593
1594 More(history);
1595
1596 return 1;
1597}
1598
1599static mixed _query_localcmds()
1600{
1601 return ({
1602 ({"kobold", "cmd_kobold",0,0}),
1603 ({"sag","_communicate",0,0}),
1604 ({"sage","_communicate",0,0}),
1605 ({"'","_communicate",1,0}),
1606 ({"mruf","_shout_to_wizards",0,0}),
1607 ({"mrufe","_shout_to_wizards",0,0}),
1608 ({"ruf","_shout_to_all",0,0}),
1609 ({"rufe","_shout_to_all",0,0}),
1610 ({"erzaehl","_erzaehle",0,0}),
1611 ({"erzaehle","_erzaehle",0,0}),
1612 ({"teil","_teile",0,0}),
1613 ({"teile","_teile",0,0}),
1614 ({"tm","_teile_mit_alias",0,0}),
1615 ({"fluester","_whisper",0,0}),
1616 ({"fluestere","_whisper",0,0}),
1617 ({"rfluester","_remote_whisper",0,0}),
1618 ({"rfluestere","_remote_whisper",0,0}),
1619 ({"gespraech","_converse",0,0}),
1620 ({"echo","_echo",0,0}),
1621 ({"ignorier","ignoriere",0,0}),
1622 ({"ignoriere","ignoriere",0,0}),
1623 ({"tmhist","tmhist",0,0}),
1624 ({"erwider","erwidere",0,0}),
1625 ({"erwidere","erwidere",0,0}),
1626 ({"klingelton","_msg_beep",0,0}),
1627 ({"senderwiederholung","_msg_prepend",0,0}),
1628 ({"report","set_report",0,0}),
1629 })+channel::_query_localcmds();
1630}
1631
1632private string *sorted_commpartners(int reversed) {
1633 return sort_array(m_indices(tell_history),
1634 function int (string uid1, string uid2) {
1635 if (reversed)
1636 return tell_history[uid1]->time_last_msg >
1637 tell_history[uid2]->time_last_msg;
1638 else
1639 return tell_history[uid1]->time_last_msg <=
1640 tell_history[uid2]->time_last_msg;
1641 } );
1642}
1643
1644// Eigentlich nur in Magierobjekten gerufen. Gehoert aber thematisch hier
1645// irgendwie hin.
1646static void modify_prompt() {
1647 string text = Query(P_PROMPT, F_VALUE);
1648
1649 if ( !stringp(text) || !sizeof(text) )
1650 text = "> ";
1651 else {
1652 string path = Query(P_CURRENTDIR, F_VALUE);
1653 if (stringp(path) && sizeof(path))
1654 text = regreplace(text,"\\w",path,0); // Pfad einsetzen
1655 }
1656 configure_interactive(this_object(), IC_PROMPT, text);
1657}
1658
1659// Prueft auf Ingoriereeintraege.
1660// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
1661#ifdef __LPC_UNIONS__
1662public int TestIgnore(string|string* srcnames)
1663#else
1664public int TestIgnore(mixed srcnames)
1665#endif
1666{
1667 mapping ign = Query(P_IGNORE, F_VALUE);
1668 if (stringp(srcnames))
1669 srcnames = ({srcnames});
1670
1671 foreach(string srcname: srcnames)
1672 {
1673 // einfachster Fall, exakter Match
1674 if (member(ign, srcname))
1675 return MSG_IGNORED;
1676 // ansonsten muss aufgetrennt werden.
1677 if (strstr(srcname,".") > -1)
1678 {
1679 string *srcparts=explode(srcname,".");
1680 switch(sizeof(srcparts))
1681 {
1682 // case 0 und 1 kann nicht passieren.
1683 case 3:
1684 // zu pruefen: [sender].aktion.qualifizierer.
1685 // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
1686 // oben schon geprueft. im Spieler geprueft werden muss noch:
1687 // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
1688 if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
1689 || member(ign, "."+srcparts[1]) // .aktion
1690 || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
1691 || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
1692 )
1693 {
1694 return MSG_IGNORED;
1695 }
1696 break;
1697 case 2:
1698 // zu pruefen: spieler.aktion
1699 // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
1700 // oben schon geprueft. Im Spieler zu pruefen ist noch:
1701 // spieler und .aktion
1702 if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
1703 || member(ign, "."+srcparts[1]))
1704 {
1705 return MSG_IGNORED;
1706 }
1707 break;
1708 default: // mehr als 3 Teile...
1709 raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
1710 "is supported. Got: %s\n",srcname));
1711 break;
1712 }
1713 }
1714 }
1715 // Default: nicht ignorieren.
1716 return 0;
1717}
1718
1719#ifdef __LPC_UNIONS__
1720public int TestIgnoreExt(string|string* srcnames)
1721#else
1722public int TestIgnoreExt(mixed srcnames)
1723#endif
1724{
1725 return TestIgnore(srcnames);
1726}
1727
1728// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
1729// Typen.
1730// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
1731private int check_ignores(string msg, int msg_type, string msg_action,
1732 string msg_prefix, object origin)
1733{
1734 // Einige Dinge lassen sich nicht ignorieren.
1735 if (msg_type & (MT_NEWS|MT_NOTIFICATION))
1736 return 0;
1737 // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
1738 // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
1739 // eine ignorierbare msg_action geben.
1740 else if (stringp(msg_action) && origin && origin != ME)
1741 {
1742 string srcname =
1743 (query_once_interactive(origin) ? origin->query_real_name()
1744 : origin->name(WER) || "");
1745 mapping ign = Query(P_IGNORE, F_VALUE);
1746
1747 if (member(ign, srcname))
1748 return MSG_IGNORED;
1749 // vielleicht wird irgendwas a la name.aktion ignoriert?
1750 // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
1751 // Ebenenmeldungen)
1752 if (member(ign, srcname+"."+msg_action))
1753 return MSG_VERB_IGN;
1754 // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
1755 // das reichlich sinnfrei ist.
1756 if (member(ign, "."+msg_action))
1757 return MSG_VERB_IGN;
1758 // Spieler auf Ebenen ignoriert?
1759 // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
1760 if (strstr(msg_action, MA_CHANNEL) == 0)
1761 {
1762 // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
1763 if (member(ign, srcname + "."MA_CHANNEL))
1764 return MSG_IGNORED;
1765 // spieler.ebenen.ebenenname ist oben schon abgedeckt.
1766 // .ebenen halte ich fuer sinnfrei, nicht geprueft.
1767 }
1768 // Spieler aus anderem mud? *seufz*
1769 if (strstr(srcname,"@") > -1)
1770 {
1771 string *srcparts = explode(srcname,"@");
1772 if (sizeof(srcparts)==2)
1773 {
1774 // spieler@?
1775 if (member(ign, srcparts[0]+"@"))
1776 return MSG_IGNORED;
1777 // oder Mud per @mud?
1778 if (member(ign, "@" + srcparts[1]))
1779 return MSG_MUD_IGN;
1780 // BTW: spieler@mud wurde schon ganz oben erfasst.
1781 }
1782 }
1783 }
1784 // Default: nicht ignorieren.
1785 return 0;
1786}
1787
1788// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
1789// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
1790// BLindheit/Taubheit).
1791// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
1792// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
1793// Sinn gar nix zu tun hat.
1794// Rueckgabe: 0 oder MSG_SENSE_BLOCK
1795private int check_senses(string msg, int msg_type, string msg_action,
1796 string msg_prefix, object origin)
1797{
1798 int senses = msg_type & (MT_LOOK|MT_LISTEN);
1799 // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
1800 // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
1801 // unterdrueckt werden.
1802 if (!senses)
1803 return 0;
1804
1805 if ((senses & MT_LOOK) && CannotSee(1))
1806 senses &= ~MT_LOOK; // Sinn loeschen
1807
1808 if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
1809 senses &= ~MT_LISTEN;
1810
1811 // wenn kein Sinn mehr ueber, wird die Nachricht nicht wahrgenommen.
1812 if (!senses)
1813 return MSG_SENSE_BLOCK;
1814
1815 return 0;
1816}
1817
1818public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
1819 string msg_prefix, object origin)
1820{
1821 if (!msg) return MSG_FAILED;
1822
1823 // Flags und Typen spalten
1824 int flags = msg_type & MSG_ALL_FLAGS;
1825 int type = msg_type & ~flags;
1826
1827 // ggf. defaults ermitteln
1828 origin ||= previous_object();
1829 msg_action ||= comm_guess_action();
1830 type ||= comm_guess_message_type(msg_action, origin);
1831
1832 // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
1833 if (msg_type & MT_DEBUG)
1834 {
1835 if (!QueryProp(P_WIZ_DEBUG)
1836 || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
1837 return MSG_FAILED;
1838 }
1839
1840 // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
1841 // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
1842 // Weiterbearbeitung.
1843 // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
1844 if (!(flags & MSG_DONT_IGNORE))
1845 {
1846 // Sinne pruefen. (nur typen uebergeben, keine Flags)
1847 int res=check_senses(msg, type, msg_action, msg_prefix, origin);
1848 if (res) return res;
1849
1850 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
1851 res=check_ignores(msg, type, msg_action, msg_prefix, origin);
1852 if (res) return res;
1853 }
1854
1855 // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
1856 if ((type & MT_COMM))
1857 {
1858 // erstmal in der Komm-History ablegen, wenn gewuenscht.
1859 if ((!(flags & MSG_DONT_STORE)))
1860 {
1861 string uid;
1862 if (query_once_interactive(origin))
1863 uid = origin->query_real_name();
1864 else
1865 uid = origin->name(WER) || "<unbekannt>";
1866 add_to_tell_history(uid, 0, 1, msg, msg_prefix, 0);
1867 }
1868
1869 // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
1870 // Ebenenmeldungen. (Die haben ggf. schon.)
1871 if (stringp(msg_action) && QueryProp(P_AWAY)
1872 && strstr(msg_action, MA_CHANNEL) != 0)
1873 {
1874 // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
1875 if (msg[<1] == '\n')
1876 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1877 else
1878 msg = msg + " [" + strftime("%H:%M",time()) + "]";
1879 }
1880 // Kobold erlaubt und gewuenscht? Kobold ist fuer die
1881 // direkte Kommunikation mittels MT_COMM vorgesehen.
1882 // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
1883 // ignorieren.
1884 // die if-Konstruktion ist so, weil ich das _flush_cache() im else
1885 // brauche.
1886 if (query_editing(this_object()) || query_input_pending(this_object())
1887 || QueryProp(P_EARMUFFS))
1888 {
1889 if (!(flags & MSG_DONT_BUFFER)
1890 && QueryProp(P_BUFFER))
1891 {
1892 // Nachricht soll im Kobold gespeichert werden.
1893 return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
1894 }
1895 }
1896 else
1897 {
1898 // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
1899 // entleeren.
1900 _flush_cache(0);
1901 }
1902
1903 // ggf. Piepston anhaengen. NACH Koboldablage, die sollen erstmal keinen
1904 // Pieps kriegen.
1905 if (comm_beep())
1906 msg=msg + MESSAGE_BEEP;
1907 }
1908
1909 // Ausgabenachricht bauen und an den Spieler senden.
1910 if (flags & MSG_DONT_WRAP)
1911 msg = (msg_prefix ? msg_prefix : "") + msg;
1912 else
1913 {
1914 int bsflags = flags & MSG_ALL_BS_FLAGS;
1915 if (QueryProp(P_MESSAGE_PREPEND))
1916 bsflags |= BS_PREPEND_INDENT;
1917 msg = break_string(msg, 78, msg_prefix, bsflags);
1918 }
1919 efun::tell_object(ME, msg);
1920
1921 return MSG_DELIVERED;
1922}