blob: 560c64a3d8ff10f2c6abddce7e61767968cb704a [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
MG Mud User88f12472016-06-24 23:31:02 +02009//#pragma range_check
10
11inherit "/std/living/comm";
12inherit "/std/player/channel";
13inherit "/std/player/comm_structs";
14
15#include <input_to.h>
16
17#define NEED_PROTOTYPES
18#include <player/quest.h>
19#include <player/gmcp.h>
20#include <living/description.h>
21#undef NEED_PROTOTYPES
22
23#include <sys_debug.h>
24
25#include <thing/properties.h>
26#include <player/comm.h>
27#include <player/base.h>
28
29#include <properties.h>
30#include <config.h>
31#include <ansi.h>
32#include <wizlevels.h>
33#include <language.h>
34#include <udp.h>
35#include <defines.h>
36#include <daemon.h>
37#include <strings.h>
38#include <regexp.h>
39#include <interactive_info.h>
40
41#define TELLHIST_DISABLED 0
42#define TELLHIST_NO_MESSAGE 1
43#define TELLHIST_ENABLED 2
44#define TELLHIST_LONGLIFE 3
45
46#define ECHO_COST 50
47#define ERWIDER_PARAM ","
48
49#define ZDEBUG(x) if (find_player("zesstra"))\
50 efun::tell_object(find_player("zesstra"),"CommDBG: "+x+"\n")
51
52private int tell_history_enabled = TELLHIST_NO_MESSAGE;
53private nosave mapping tell_history=([]);
54private nosave string *commreceivers = ({});
55private nosave string last_comm_partner;
56private nosave int last_beep_time;
57
58// Statusreporte aktiviert? Binaere Flags (s. set_report())
59private int stat_reports;
60// interner Cache fuer die LP/KP/Gift-Werte fuer die Statusreport-Ausgaben
61// Eintraege (in dieser Reihenfolge): P_HP, P_SP, Giftstatus
62// Initialisierung erfolgt beim ersten Report nach Login
63private nosave mixed *report_cache;
64
65// Puffer fuer Kobold.
66private nosave struct msg_buffer_s kobold = (<msg_buffer_s>
67 buf: allocate(32),
68 index: -1,);
69#define MAX_KOBOLD_LIMIT 256
70
71varargs string name(int casus, int demonst);
72
73//local property prototypes
74static int _query_intermud();
75public int RemoveIgnore(string ign);
76public int AddIgnore(string ign);
77
78public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
79 string msg_prefix, object origin);
80
81// erzeugt sortierte Liste an Kommunikationspartnern
82private string *sorted_commpartners(int reversed);
83
84private nosave string *buffer = ({});
85
86void create()
87{
88 ::create();
89 Set(P_EARMUFFS, 0);
90 Set(P_EARMUFFS, SAVE, F_MODE);
91 Set(P_EARMUFFS, SECURED, F_MODE);
92 Set(P_INTERMUD, SAVE, F_MODE);
93 Set(P_IGNORE, ([]), F_VALUE);
94 Set(P_IGNORE, SAVE, F_MODE);
95 Set(P_BUFFER, SAVE, F_MODE);
96 Set(P_MESSAGE_PREPEND, SAVE, F_MODE_AS);
97 Set(P_MESSAGE_BEEP, SAVE, F_MODE_AS);
98}
99
100void create_super()
101{
102 set_next_reset(-1);
103}
104
105// uebermittelt eine MT_NOTIFICATION an this_object(), welche nicht ignoriert
106// werden kann und auch nicht gespeichert wird.
107protected void _notify(string msg, string action) {
108 ReceiveMsg(msg,
109 MT_NOTIFICATION|MSG_DONT_BUFFER|MSG_DONT_IGNORE|MSG_DONT_STORE,
110 action, 0, this_object());
111}
112
113protected void updates_after_restore(int newflag) {
114 // Altes Ignoriere loeschen...
115 mixed ign = Query(P_IGNORE,F_VALUE);
116 if (!mappingp(ign))
117 {
118 if (pointerp(ign))
119 _notify(break_string(
120 "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
121 "weil es eine Aktualisierung der Ignorierefunktion gab, "
122 "bei der eine Konversion der Daten leider nicht "
123 "moeglich war.",78), 0);
124
125 Set(P_IGNORE, ([]), F_VALUE);
126 }
127}
128
129static int set_report(string str) {
130 int canflags = QueryProp(P_CAN_FLAGS);
131
132 if(!str)
133 {
134 if (stat_reports) {
135 string *res=({});
136 if (stat_reports & DO_REPORT_HP)
137 res+=({"Lebenspunkte"});
138 if (stat_reports & DO_REPORT_SP)
139 res+=({"Konzentrationspunkte"});
140 if (stat_reports & DO_REPORT_POISON)
141 res+=({"Vergiftungen"});
142 if (stat_reports & DO_REPORT_WIMPY)
143 res+=({"Vorsicht"});
144
145 tell_object(ME,break_string(
146 "Dir werden jetzt Veraenderungen Deiner "
147 +CountUp(res) + " berichtet.",78));
148 }
149 else
150 tell_object(ME,
151 "Alle Statusreports sind ausgeschaltet.\n");
152
153 return 1;
154 }
155 else if (str == "aus") {
156 if (stat_reports & DO_REPORT_HP || stat_reports & DO_REPORT_WIMPY) {
157 string s="";
158 if (stat_reports & DO_REPORT_HP) {
159 str="ebenfalls ";
160 tell_object(ME, "Der Report wurde ausgeschaltet.\n");
161 }
162 if ( stat_reports & DO_REPORT_WIMPY ) {
163 tell_object(ME, "Der Vorsicht-Report wurde "+s+
164 "ausgeschaltet.\n");
165 }
166 stat_reports=0;
167 }
168 else {
169 tell_object(ME, "Der Report ist bereits ausgeschaltet.\n");
170 }
171 return 1;
172 }
173 else if (str == "ein") {
174 if ( stat_reports & DO_REPORT_HP ) {
175 tell_object(ME, "Der Report ist bereits eingeschaltet.\n");
176 return 1;
177 }
178 tell_object(ME, "Der Report wurde eingeschaltet.\n");
179 stat_reports |= DO_REPORT_HP;
180 if (!(canflags & CAN_REPORT_SP)) {
181 if (QueryQuest("Hilf den Gnarfen")==1) {
182 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_SP);
183 stat_reports |= DO_REPORT_SP;
184 }
185 else {
186 tell_object(ME, break_string(
187 "Fuer den Statusreport Deiner Konzentration musst Du jedoch "
188 "zunaechst die Quest \"Hilf den Gnarfen\" bestehen.",78));
189 }
190 }
191 else {
192 stat_reports |= DO_REPORT_SP;
193 }
194 if (!(canflags & CAN_REPORT_POISON)) {
195 if (QueryQuest("Katzenjammer")==1) {
196 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_POISON);
197 stat_reports |= DO_REPORT_POISON;
198 }
199 else {
200 tell_object(ME, break_string(
201 "Fuer den Statusreport Deiner Vergiftung musst Du jedoch "
202 "zunaechst die Quest \"Katzenjammer\" bestehen.",78));
203 }
204 }
205 else {
206 stat_reports |= DO_REPORT_POISON;
207 }
208 // Cache loeschen, damit beim naechsten Report-Event alle Daten neu
209 // eingetragen werden muessen. Muss beim Einschalten des Reports
210 // passieren, weil auch in der inaktiven Zeit weiterhin Aenderungen in
211 // status_report() eingehen, so dass der Cache zwar erst einmal leer ist,
212 // aber beim Wiedereinschalten nicht mehr ungueltig waere und somit
213 // veraltete Daten an den Spieler ausgegeben werden. Im unguenstigsten
214 // Fall wuerde das sogar dazu fuehren, dass die veralteten Daten lange
215 // Zeit nicht aktualisiert werden, wenn z.B. P_HP == P_MAX_HP, so dass
216 // kein P_HP-Event mehr eingeht.
217 report_cache=0;
218 }
219 else if (str == "vorsicht") {
220 if (!(canflags & CAN_REPORT_WIMPY)) {
221 if (QueryQuest("Schrat kann nicht einschlafen")==1) {
222 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_WIMPY);
223 tell_object(ME, "Der Vorsicht-Report wurde eingeschaltet.\n");
224 stat_reports |= DO_REPORT_WIMPY;
225 }
226 else {
227 tell_object(ME, break_string(
228 "Fuer den Statusreport Deiner Vorsicht musst Du "
229 "zunaechst die Quest \"Schrat kann nicht einschlafen\" "
230 "bestehen.",78));
231 }
232 }
233 else
234 {
235 stat_reports |= DO_REPORT_WIMPY;
236 }
237 // fuer Seher auch Bericht der Fluchtrichtung einschalten.
238 if ((stat_reports & DO_REPORT_WIMPY)
239 && !(stat_reports & DO_REPORT_WIMPY_DIR)
240 && ((canflags & CAN_REPORT_WIMPY) || IS_SEER(ME)))
241 {
242 stat_reports |= DO_REPORT_WIMPY_DIR;
243 }
244 }
245 // sendet einmalig genau jetzt den konfigurierten report. Kann zum testen
246 // (von Triggern) oder beim Login benutzt werden, wenn man einen initialen
247 // Datenbestand erhalten will.
248 else if (str=="senden")
249 {
250 // Es wird Ausgabe von LP und Vorsicht getriggert, das sendet beide
251 // Zeilen.
252 status_report(DO_REPORT_HP, QueryProp(P_HP));
253 status_report(DO_REPORT_WIMPY, QueryProp(P_WIMPY));
254 return 1;
255 }
256 else
257 return 0;
258 // nur aktuellen Zustand berichten
259 set_report(0);
260 return 1;
261}
262
263private string get_poison_desc(int p) {
264 string ret;
265 if ( intp(p) ) {
266 switch(p) {
267 case 0: ret="keins"; break;
268 case 1..3: ret="leicht"; break;
269 case 4..8: ret="gefaehrlich"; break;
270 default: ret="sehr ernst"; break;
271 }
272 return ret;
273 }
274 else return "(nicht verfuegbar)";
275}
276
277// sprintf()-Formatstrings fuer die Reportausgabe.
278#define REPORTLINE "LP: %3d, KP: %3s, Gift: %s.\n"
279#define REPORTLINE_WIMPY "Vorsicht: %d, Fluchtrichtung: %s.\n"
280// Defines zur Adressierung der Cache-Eintraege
281#define REP_HP 0
282#define REP_SP 1
283#define REP_POISON 2
284
285protected void status_report(int type, mixed val) {
286 // Wenn der Spieler GMCP hat und das sich um die Information kuemmert,
287 // erfolgt keine textuelle Ausgabe mehr. Daher return, wenn GMCP_Char()
288 // erfolg vermeldet hat.
289 int flags = QueryProp(P_CAN_FLAGS);
290 switch (type) {
291 case DO_REPORT_HP:
292 if (GMCP_Char( ([ P_HP: val ]) ) ) return;
293 break;
294 case DO_REPORT_SP:
295 if (!(flags & CAN_REPORT_SP)) return;
296 if (GMCP_Char( ([ P_SP: val ]) ) ) return;
297 break;
298 case DO_REPORT_POISON:
299 if (!(flags & CAN_REPORT_POISON)) return;
300 if (GMCP_Char( ([ P_POISON: val ]) ) ) return;
301 break;
302 case DO_REPORT_WIMPY:
303 if (!(flags & CAN_REPORT_WIMPY)) return;
304 if (GMCP_Char( ([ P_WIMPY: val ]) ) ) return;
305 break;
306 case DO_REPORT_WIMPY_DIR:
307 if (!(flags & CAN_REPORT_WIMPY_DIR)) return;
308 if (GMCP_Char( ([ P_WIMPY_DIRECTION: val ]) ) ) return;
309 break;
310 }
311
312 // konventionelle textuelle Ausgabe des Reports ab hier.
313 if (!(type & stat_reports))
314 return;
315
316 if ( !report_cache ) {
317 report_cache = ({
318 QueryProp(P_HP),
319 (stat_reports&DO_REPORT_SP) ? to_string(QueryProp(P_SP)) : "###",
320 (stat_reports&DO_REPORT_POISON) ?
321 get_poison_desc(QueryProp(P_POISON)) : "(nicht verfuegbar)"
322 });
323 }
324
325 switch(type) {
326 // LP berichten: Cache aktualisieren und Meldung ausgeben.
327 case DO_REPORT_HP:
328 report_cache[REP_HP]=val;
329 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
330 report_cache[REP_SP], report_cache[REP_POISON]));
331 break;
332 // KP berichten: Wenn der Spieler den Report freigeschaltet hat,
333 // wird bei Aenderungen gemeldet. Wenn nicht, aendert sich nur der
334 // Cache-Eintrag. So wird verhindert, dass ein Spieler ueber KP-
335 // Veraenderungen auch dann informiert wuerde, wenn er den KP-Report
336 // gar nicht benutzen koennte.
337 case DO_REPORT_SP:
338 report_cache[REP_SP]=to_string(val);
339 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
340 report_cache[REP_SP], report_cache[REP_POISON]));
341 break;
342 // Giftstatus berichten: Wenn der Giftreport freigeschaltet ist,
343 // Cache aktualisieren und berichten. Wenn nicht, aendert sich nur
344 // der Cache-Eintrag. Erlaeuterung hierzu s.o. beim KP-Report.
345 case DO_REPORT_POISON:
346 report_cache[REP_POISON] = get_poison_desc(val);
347 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
348 report_cache[REP_SP], report_cache[REP_POISON]));
349 break;
350 // Vorsicht-Report: kann ohne weitere Abfragen ausgegeben werden, da
351 // alle noetigen Checks schon zu Beginn dieser Funktion erledigt wurden.
352 // Lediglich der Inhalt der Meldung muss abhaengig vom Seherstatus
353 // konfiguriert werden.
354 case DO_REPORT_WIMPY:
355 string res;
356 if (IS_SEER(ME)) {
357 // QueryProp() aus Kostengruenden im if(), damit die Aufruf-
358 // Haeufigkeit zumindest ein wenig reduziert wird.
359 string dir = QueryProp(P_WIMPY_DIRECTION)||"keine";
360 res = sprintf(REPORTLINE_WIMPY, val, dir);
361 }
362 else
363 res = sprintf(REPORTLINE_WIMPY, val, "(nicht verfuegbar)");
364 tell_object(ME, res);
365 break;
366 // Fluchtrichtungs-Report: wird nur bei Sehern ausgegeben, damit
367 // nicht auch Spieler eine VS-/FR-Meldung bekommen, wenn z.B. eine
368 // externe Manipulation der Fluchtrichtung stattfindet, sie aber den
369 // Report mangels Seherstatus gar nicht freigeschaltet haben.
370 case DO_REPORT_WIMPY_DIR:
371 if (IS_SEER(ME)) {
372 if (!val) val = "keine";
373 tell_object(ME,sprintf(REPORTLINE_WIMPY, QueryProp(P_WIMPY), val));
374 }
375 break;
376 }
377}
378
379#undef REPORTLINE
380#undef REPORTLINE_WIMPY
381#undef REP_HP
382#undef REP_SP
383#undef REP_POISON
384
385private string permutate(string msg)
386{
387 // Kontrollzeichen rausfiltern. *seufz*
388 msg = regreplace(msg,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
389 object ob=QueryProp(P_PERM_STRING);
390 if (!objectp(ob))
391 return msg;
392
Zesstra04f613c2019-11-27 23:32:54 +0100393 return ({string})ob->permutate_string(msg)||"";
MG Mud User88f12472016-06-24 23:31:02 +0200394}
395
396// neue nachricht an den Kobold anhaengen
397// Rueckgabewerte: MSG_BUFFER_FULL oder MSG_BUFFERED
398private int add_to_kobold(string msg, int msg_type, string msg_action,
399 string msg_prefix, object origin)
400{
401 // Nachricht soll im Kobold gespeichert werden.
402 // Kobold speichert Rohdaten und gibt spaeter das ganze auch wieder via
403 // ReceiveMsg() aus - dabei wird MSG_DONT_BUFFER | MSG_DONT_STORE gesetz,
404 // damit keine erneute Speicher in Kobold oder Komm-History erfolgt.
405
406 // wenn der Puffer zu klein ist, Groesse verdoppeln, wenn noch unterhalb
407 // des Limits.
408 if (kobold->index >= sizeof(kobold->buf)-1) {
409 if (sizeof(kobold->buf) < MAX_KOBOLD_LIMIT)
410 kobold->buf += allocate(sizeof(kobold->buf));
411 else
412 return MSG_BUFFER_FULL;
413 }
414 kobold->index = kobold->index +1;
415 // neue Nachricht an den Puffer anhaengen.
416 string sendername = query_once_interactive(origin) ?
417 origin->query_real_name() :
418 origin->name(WER) || "<Unbekannt>";
419 kobold->buf[kobold->index] = (<msg_s> msg: msg,
420 type : msg_type, action : msg_action, prefix : msg_prefix,
421 sendername : sendername);
422 return MSG_BUFFERED;
423}
424
425private void _flush_cache(int verbose) {
426 // nur mit genug Evalticks ausgeben.
427 if (get_eval_cost() < 100000) return;
428 if (kobold->index >= 0)
429 {
430 ReceiveMsg("Ein kleiner Kobold teilt Dir folgendes mit:",
431 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
432 0, 0, this_object());
433 int prepend = QueryProp(P_MESSAGE_PREPEND);
434 foreach(int i: 0 .. kobold->index) // '0 ..' ist wichtig!
435 {
436 struct msg_s msg = kobold->buf[i];
437 // dies ist dient der Fehlerabsicherung, falls es nen Fehler (z.B. TLE)
438 // in der Schleife unten gab: dann ist index nicht auf -1 gesetzt
439 // worden, aber einige Nachrichten sind schon geloescht.
440 if (!structp(msg)) continue;
441 // Ausgabe via efun::tell_object(), weil die Arbeit von ReceiveMsg()
442 // schon getan wurde. Allerdings muessen wir uns noch um den UMbruch
443 // kuemmern.
444 if ((msg->type) & MSG_DONT_WRAP)
445 msg->msg = (msg->prefix ? msg->prefix : "") + msg->msg;
446 else
447 {
448 int bsflags = msg->type & MSG_ALL_BS_FLAGS;
449 if (prepend)
450 bsflags |= BS_PREPEND_INDENT;
451 msg->msg = break_string(msg->msg, 78, msg->prefix, bsflags);
452 }
453 efun::tell_object(this_object(), msg->msg);
454 kobold->buf[i]=0;
455 }
456 kobold->index=-1;
457 }
458 else if (verbose)
459 {
460 ReceiveMsg("Der kleine Kobold hat leider nichts Neues fuer Dich.",
461 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
462 0, 0, this_object());
463 }
464}
465
466varargs int cmd_kobold(string arg)
467{
468 switch(arg)
469 {
470 case "ein":
471 SetProp(P_BUFFER, 1);
472 printf("Der Kobold merkt sich jetzt alles!\n"); break;
473 case "aus":
474 SetProp(P_BUFFER, 0);
475 printf("Der Kobold wird Dich nicht stoeren!\n"); break;
476 default: if(arg) printf("Der Kobold sagt: kobold ein oder kobold aus\n");
477 }
478 _flush_cache(1);
479 return 1;
480}
481
482public int TestIgnoreSimple(string *arg)
Zesstrade642d22019-11-23 17:22:34 +0100483{ mapping ignore;
MG Mud User88f12472016-06-24 23:31:02 +0200484
485 if (!pointerp(arg) || !mappingp(ignore=Query(P_IGNORE,F_VALUE)))
486 return 0;
487 foreach(string s: arg)
488 {
489 if (member(ignore,s))
490 return 1;
491 }
492 return 0;
493}
494
495//TODO: deprecated - entfernen, wenn Message() entfernt wird.
496private int check_ignore(mixed ignore, string verb, string name)
497{
498 if (ignore == verb)
499 return 1;
500 ignore = explode(ignore, ".");
501 return ((sizeof(ignore) > 1) &&
502 (name == ignore[0] && member(ignore[1..], verb) != -1));
503}
504
505private int comm_beep() {
506 if (QueryProp(P_VISUALBELL)) return 0; // kein ton
Zesstra04f613c2019-11-27 23:32:54 +0100507 int beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
MG Mud User88f12472016-06-24 23:31:02 +0200508 if (!beep_interval || ((time()-last_beep_time) < beep_interval)) return 0;
509 last_beep_time=time();
510 return 1;
511}
512
513private varargs void add_to_tell_history( string uid, int sent, int recv,
514 string message, string indent, int flags )
515{
516 /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
517 Als Wert ist eine Strukur vom Typ chat_s eingetragen.
518 Strukturen chat_s und stored_msg_s sind in /std/player/comm_structs.c
519 definiert.
520 TODO fuer spaeter, gerade keine Zeit fuer:
521 Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
522 jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
523 weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
524 Element ist aeltestes Gespraech).
525 */
526
527 //TODO: Entfernen, wenn das nicht mehr passiert.
528 if (!stringp(uid))
529 {
530 ReceiveMsg(sprintf(
531 "\nadd_to_tell_history(): got bad uid argument %O."
532 "sent: %d, recv: %d, flags: %d, msg: %s",
533 uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);
534 }
Zesstraa31cd5c2016-12-17 20:11:07 +0100535
MG Mud User88f12472016-06-24 23:31:02 +0200536 // letzten Gespraechspartner fuer erwidere.
Zesstraa31cd5c2016-12-17 20:11:07 +0100537 if (flags & (MSGFLAG_TELL|MSGFLAG_RTELL))
MG Mud User88f12472016-06-24 23:31:02 +0200538 last_comm_partner = uid;
539
540 // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
541 // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
542 // Anfang.
543 if (sent) {
544 if (!sizeof(commreceivers))
545 commreceivers = ({uid});
546 else if (commreceivers[0] != uid) {
547 // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
548 // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
549 // entfernen.
550 // TODO: Effizienter gestalten.
551 commreceivers = ({uid}) + (commreceivers-({uid}));
552 // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
553 // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
554 // commreceivers drin sind.)
555 if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
556 commreceivers = commreceivers[0..MAX_SAVED_CHATS];
557 }
558 }
559
560 if (!tell_history_enabled)
561 return;
562
563 if (!indent && message[<1] == 10)
564 message = message[..<2];
565
566 struct chat_s chat;
567 // Gespraechspartner unbekannt?
568 if (!member(tell_history, uid)) {
569 // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
570 // dazu kommt.
571 if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
572 string deluid;
573 int zeit = __INT_MAX__;
574 foreach(string tuid, chat : tell_history) {
575 // aeltestes Gespraech suchen
576 if (zeit > chat->time_last_msg) {
577 deluid = tuid;
578 zeit = chat->time_last_msg;
579 }
580 }
581 // aeltestes Gespraech raus.
582 m_delete(tell_history, deluid);
583 if (member(commreceivers,deluid)>-1)
584 commreceivers-=({deluid});
585 }
586 // neues Gespraech anlegen
587 chat = (<chat_s> uid: uid, time_first_msg: time(),
588 time_last_msg: time(),
589 sentcount: sent, recvcount: recv,
590 msgbuf: 0, ptr: 0 );
591 tell_history[uid] = chat;
592 }
593 else {
594 // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
595 chat = tell_history[uid];
596 chat->time_last_msg = time();
597 chat->sentcount += sent;
598 chat->recvcount += recv;
599 }
600
601 if (tell_history_enabled < TELLHIST_ENABLED)
602 return;
603
604 // ggf. Array fuer Messages anlegen
605 if (!pointerp(chat->msgbuf))
606 chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
607
608 // Message-Struktur ermitteln oder neu anlegen
609 struct stored_msg_s msg;
610 if (!structp(chat->msgbuf[chat->ptr])) {
611 // neue Struct ins Array schreiben
612 chat->msgbuf[chat->ptr] = msg = (<stored_msg_s>);
613 }
614 else {
615 // alte Struct ueberschreiben
616 msg = chat->msgbuf[chat->ptr];
617 }
618 // Index auf naechste Messagestruktur ermitteln
619 chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
620 // Message speichern
621 msg->msg = message;
622 msg->prefix = indent;
623 msg->timestamp = time();
624}
625
626protected void clear_tell_history()
627{
628 /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
629 sofern der Spieler nichts abweichendes eingestellt hat. */
630
631#ifdef TELLHIST_LONGLIFE
632 if (tell_history_enabled == TELLHIST_LONGLIFE)
633 return;
634#endif
635
636 foreach (string uid, struct chat_s chat: tell_history)
637 if (pointerp(chat->msgbuf)) {
638 chat->msgbuf = 0;
639 chat->ptr = 0;
640 }
641}
642
643protected void reset(void)
644{
645 /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
646 Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
647 wie bei einem "schlafe ein" die Mitteilungen geloescht. */
648
649 if (!interactive())
650 clear_tell_history();
651
652}
653
654// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
655// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
656// werden soll.
657private void _send(object ob, string msg, int msg_type,
658 string msg_action, string msg_prefix)
659{
660 int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
661 switch(res) {
662 case MSG_DELIVERED:
663 break; // nix machen
664 case MSG_BUFFERED:
665 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden."
666 "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
667 "genommen. Er wird sie spaeter weiterleiten!",
668 MT_NOTIFICATION, msg_action, 0, this_object());
669 break;
670 case MSG_IGNORED:
671 case MSG_VERB_IGN:
672 case MSG_MUD_IGN:
673 ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
674 MT_NOTIFICATION, msg_action, 0, this_object());
675 break;
676 case MSG_SENSE_BLOCK:
677 ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
678 MT_NOTIFICATION, msg_action, 0, this_object());
679 break;
680 case MSG_BUFFER_FULL:
681 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden."
682 "Die Mitteilung ging verloren, denn der Kobold kann sich "
683 "nichts mehr merken!", MT_NOTIFICATION, msg_action,
684 0, this_object());
685 break;
686 default:
687 ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
688 "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
689 break;
690 }
691}
692
693// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
694// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
695// die Nachricht ggf. in den Kommhistory erfasst wird.
696// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
697protected varargs int _recv(object ob, string message, int flag, string indent)
698{
699 write(break_string(message, 78, indent,
700 QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
701 if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
702 query_once_interactive(ob))
703 {
704 if (flag & MSGFLAG_WHISPER)
705 add_to_tell_history(getuid(ob), 1, 0,
706 "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
707 flag);
708 else
709 add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
710 }
711 return 1;
712}
713
714// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
715// Fehlerbehandlung) ist es jedoch auch mal ein String.
716varargs int Message(string msg, int flag, string indent,
717 string cname, mixed sender)
718{
719 object ti;
720 string verb, reply, *ignore, tin;
721 int em, te;
722 mixed deaf;
723
724 // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
725 // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
726 // damit 'ignoriere name.debug' funktioniert.
727 if( flag == MSGFLAG_CHANNEL ){
728 if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
729 || msg[1..9]=="Warnungen"))
730 {
731 // Missbrauch der Variable 'ignore' als Zwischenspeicher
732 ignore = regexplode( msg, ":| |\\]" );
733 verb = lower_case(ignore[0][1..]);
734 tin = lower_case(ignore[2]);
735 }
736 else
737 {
738 if(cname)
739 verb=lower_case(cname);
740 else
741 verb=query_verb();
742 if( ti = this_interactive() )
743 {
744 tin = getuid(this_interactive());
745 }
746 else
747 {
748 //falls doch kein Objekt...
749 if (objectp(sender))
750 tin=lower_case(sender->name(RAW)||"<Unbekannt>");
751 }
752 }
753 }
754 else {
755 if( ti = this_interactive() )
756 tin = getuid(this_interactive());
757 verb = query_verb();
758 }
759
760 te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
761
762 // fuer "erwidere"
763 if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
764 if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
765 if (flag & MSGFLAG_WHISPER)
766 add_to_tell_history(getuid(ti), 0, 1,
767 capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
768 (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
769 ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
770 " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
771 else
772 add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
773 }
774 }
775 // Hoert der Spieler nicht?
776 em = (ti &&
777 (te || flag & MSGFLAG_SHOUT) &&
778 (QueryProp(P_EARMUFFS) &&
779 (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
780 ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
781
782 // Werden der Sender oder das Verb ignoriert?
783 if(!ti && tin && flag == MSGFLAG_CHANNEL)
784 {
785 if((member(ignore, tin) != -1))
786 {
787 return MESSAGE_IGNORE_YOU;
788 }
789 if(verb && sizeof(filter(ignore, #'check_ignore, verb, tin)) )
790 {
791 return MESSAGE_IGNORE_YOU;
792 }
793 }
794 if (ti && (member(ignore, getuid(ti)) != -1)) {
795 if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
796 efun::tell_object(ti, capitalize(name())+
797 " hoert gar nicht zu, was Du sagst.\n");
798 return MESSAGE_IGNORE_YOU;
799 }
800 if(tin && verb &&
801 sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
802 {
803 if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
804 verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
805 efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
806 return MESSAGE_IGNORE_VERB;
807 }
808 if (flag & MSGFLAG_RTELL) {
809 int at;
810
811 verb = lower_case(old_explode(msg, " ")[0][1..]);
812 at = member(verb, '@');
813 /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
814 add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
815
816 if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
817 return MESSAGE_IGNORE_YOU;
818 else if (at > 0 && member(ignore, verb[at..]) >= 0)
819 return MESSAGE_IGNORE_MUD;
820 }
821
822 // Taubheit/Oropax
823 te |= (flag & MSGFLAG_SAY);
824
825 if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
826 deaf = QueryProp(P_DEAF);
827 if (te)
828 reply = stringp(deaf) ?
829 capitalize(sprintf(deaf, name())) :
830 capitalize(name())+" ist momentan leider taub.\n";
831 }
832 else if (em)
833 reply = capitalize(name())+" hat Oropax in den Ohren.\n";
834
835 msg = break_string(msg, 78, indent,
836 (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
837
838 if(QueryProp(P_BUFFER) &&
839 (deaf ||
840 query_editing(this_object()) ||
841 query_input_pending(this_object())))
842 {
843 deaf = MESSAGE_DEAF;
844 if(flag & MSGFLAG_CACHE)
845 {
846 if(!stringp(reply))
847 reply = name()+" moechte gerade nicht gestoert werden.\n";
848
849 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
850
851 int res = add_to_kobold(msg, 0, 0, 0,
852 objectp(sender) ? sender : ME);
853 if(res == MSG_BUFFERED)
854 {
855
856 reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
857 "genommen.\nEr wird sie spaeter weiterleiten!";
858 deaf = MESSAGE_CACHE;
859 }
860 else {
861 reply += "Die Mitteilung ging verloren, denn "+
862 "der Kobold kann sich nichts mehr merken!";
863 deaf = MESSAGE_CACHE_FULL;
864 }
865 if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
866 efun::tell_object(ti, reply+"\n");
867 }
868 return deaf;
869 }
870 else if((deaf || em) &&
871 ( (flag & MSGFLAG_RTELL) ||
872 (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
873 if (te && ti)
874 efun::tell_object(ti, reply);
875 return MESSAGE_DEAF;
876 }
877
878 _flush_cache(0);
879 if(te && QueryProp(P_AWAY))
880 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
881
882 if (flag & (MSGFLAG_SAY | MSGFLAG_TELL) && comm_beep()) {
883 msg=MESSAGE_BEEP+msg;
884 }
885 efun::tell_object(ME, msg);
886 return MESSAGE_OK;
887}
888
889static int ignoriere(string str)
890{
891 str = _unparsed_args(1);
892 mapping ignore=Query(P_IGNORE, F_VALUE);
893
894 if (!str)
895 {
896 string* ignarr = m_indices(ignore);
897 if (!sizeof(ignarr))
898 tell_object(ME, "Du ignorierst niemanden.\n");
899 else
900 ReceiveMsg("Du ignorierst:\n"
901 + break_string(CountUp(map(sort_array(ignarr, #'> ),
902 #'capitalize )
903 ) + ".",78),
904 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
905 0,0,this_object());
906 return 1;
907 }
908 // trim spaces from args and convert to lower case.
909 str = lower_case(trim(str, TRIM_BOTH));
910
911 if (member(ignore, str))
912 {
913 RemoveIgnore(str);
914 tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
915 }
916 else if (sizeof(ignore)>100)
917 {
918 tell_object(ME, "Du ignorierst schon genuegend!\n");
919 }
920 else if (AddIgnore(str) == 1)
921 {
922 tell_object(ME,
923 sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
924 }
925 else
926 {
927 tell_object(ME,
928 sprintf("'%s' kannst Du nicht ignorieren.\n",str));
929 }
930 return 1;
931}
932
933
934static int _msg_beep(string str) {
935 int beep_interval;
936 notify_fail("Syntax: klingelton <1 bis 3600 Sekunden> oder klingelton aus\n");
937 if (stringp(str)) {
938 if (str=="aus")
939 SetProp(P_MESSAGE_BEEP,0);
940 else if ((beep_interval=to_int(str)) > 0 && beep_interval<=3600)
941 SetProp(P_MESSAGE_BEEP,beep_interval);
942 else return 0;
943 }
944
Zesstra593b2c72019-11-27 23:37:03 +0100945 beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
MG Mud User88f12472016-06-24 23:31:02 +0200946 _notify("Ton bei Mitteilungen: "+
947 (beep_interval ? "aller "+beep_interval+" Sekunden." : "aus."),
948 query_verb());
949 return 1;
950}
951
952static int _msg_prepend(string str) {
953 int beep_interval;
954 notify_fail("Syntax: senderwiederholung ein/aus\n");
955 if (stringp(str)) {
956 if (str=="aus")
957 SetProp(P_MESSAGE_PREPEND,1);
958 else if (str=="ein")
959 SetProp(P_MESSAGE_PREPEND,0);
960 else return 0;
961 }
962
963 _notify("Senderwiederholung bei Mitteilungen: "+
Zesstra04f613c2019-11-27 23:32:54 +0100964 (({int})QueryProp(P_MESSAGE_PREPEND) ? "aus" : "ein")+".",
MG Mud User88f12472016-06-24 23:31:02 +0200965 query_verb());
966
967 return 1;
968}
969
970static int _communicate(mixed str, int silent)
971{
972 string verb;
973 string myname;
974 string msg;
975
976 if (!str || extern_call()) str=_unparsed_args()||"";
977 /* str=_unparsed_args()||""; */
978 verb = query_verb();
979 if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
980 if (str==""||str==" "||!str)
981 {
982 _notify("Was willst Du sagen?",MA_SAY);
983 return 1;
984 }
985 msg=permutate(str);
986
987 myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
988 !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
989 "":QueryProp(P_PRESAY))+name())||"";
990
991 // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
992 // ist, keine MT_COMM)
993 send_room(environment(), msg, MT_LISTEN, MA_SAY,
994 capitalize(myname)+" sagt: ", ({this_object()}) );
995
996 if(!silent)
997 {
998 ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
999 MA_SAY, "Du sagst: ", ME);
1000 }
1001 return 1;
1002}
1003
1004static int _shout_to_all(mixed str)
1005{
1006 string pre, myname, realname, wizards_msg, players_msg;
1007 string wizard_prefix, player_prefix;
1008 int chars;
1009
1010 if (!(str=_unparsed_args()))
1011 {
1012 _notify("Was willst Du rufen?",MA_SHOUT);
1013 return 1;
1014 }
1015 chars=sizeof(str)/2;
1016 if (chars<4) chars=4;
1017 pre = (!IS_LEARNER(ME) ||
1018 QueryProp(P_INVIS) ||
1019 !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
1020 realname = capitalize((pre + capitalize(getuid()))||"");
1021 myname = capitalize(pre + name()||"");
1022 if (QueryProp(P_INVIS))
1023 realname = "("+realname+")";
1024
1025 wizards_msg = permutate(str);
1026 wizard_prefix = myname+" ruft: ";
1027
1028 if(QueryProp(P_FROG)) {
1029 players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
1030 player_prefix = myname+" quakt: ";
1031 }
1032 else {
1033 players_msg = wizards_msg;
1034 player_prefix = wizard_prefix;
1035 }
1036
1037 if(!IS_LEARNER(this_player()))
1038 {
1039 if(QueryProp(P_GHOST)) {
1040 _notify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
1041 MA_SHOUT);
1042 return 1;
1043 }
1044 if (QueryProp(P_SP) <(chars+20))
1045 {
1046 _notify("Du musst erst wieder magische Kraefte sammeln.",
1047 MA_SHOUT);
1048 _notify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
1049 "Ebenen').", MA_SHOUT);
1050 return 1;
1051 }
1052 SetProp(P_SP, QueryProp(P_SP) - chars - 20);
1053 }
1054
1055 ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1056 "rufe", "Du rufst: ", ME);
1057
1058 foreach ( object ob : users()-({this_object()}) )
1059 if ( IS_LEARNER(ob) )
1060 ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR, MA_SHOUT, wizard_prefix,
1061 this_object());
1062 else
1063 ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR, MA_SHOUT, player_prefix,
1064 this_object());
1065
1066 return 1;
1067}
1068
1069varargs int _tell(string who, mixed msg)
1070{
1071 object ob;
1072 string away,myname,ret;
1073 mixed ignore,it;
1074 string *xname;
1075 int i,visflag;
1076
1077 if (extern_call() && this_interactive()!=ME) return 1;
1078 if (!who || !msg) {
1079 _notify("Was willst Du mitteilen?",MA_TELL);
1080 return 1;
1081 }
1082
1083 if(who == ERWIDER_PARAM)
1084 {
1085 if (!last_comm_partner)
1086 {
1087 _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
1088 "Du was erwidern\nkoenntest.\n");
1089 return 0;
1090 }
1091 who=last_comm_partner;
1092 }
1093
1094 // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
1095 if (who == ".")
1096 who = ".1";
1097
1098 if ( sscanf(who, ".%d", i) == 1 ) {
1099 if(i > 0 && i <= sizeof(commreceivers))
1100 who = commreceivers[i-1];
1101 else {
1102 _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
1103 return 0;
1104 }
1105 }
1106
1107 xname = explode(who, "@");
1108
1109 if (sizeof(xname) == 2)
1110 {
1111 if ( QueryProp(P_QP) )
1112 {
Zesstra04f613c2019-11-27 23:32:54 +01001113 if (ret=({string})INETD->_send_udp(xname[1],
MG Mud User88f12472016-06-24 23:31:02 +02001114 ([ REQUEST: "tell",
1115 RECIPIENT: xname[0],
1116 SENDER: getuid(ME),
1117 DATA: msg ]), 1))
1118 {
1119 _notify(ret, MA_TELL);
1120 }
1121 else
1122 {
1123 write("Nachricht abgeschickt.\n");
1124 add_to_tell_history(who, 1, 0, msg,
Zesstraa31cd5c2016-12-17 20:11:07 +01001125 "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_RTELL, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001126 }
1127 }
1128 else
1129 write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
1130 "Muds etwas mitteilen zu koennen.\n");
1131 return 1;
1132 }
1133
1134 if (!ob=find_player(it = lower_case(who)))
1135 {
1136 it = match_living(it, 0);
1137 if (!stringp(it))
1138 switch(it) {
1139 case -1:
1140 _notify("Das war nicht eindeutig!",MA_TELL);
1141 return 1;
1142 case -2:
1143 _notify("Kein solcher Spieler!",MA_TELL);
1144 return 1;
1145 }
1146 ob = find_player(it) || find_living(it);
1147 if(!ob) {
1148 _notify("Kein solcher Spieler!",MA_TELL);
1149 return 1;
1150 }
1151 }
1152
1153 if(QueryProp(P_INVIS)){
1154 if(!IS_LEARNER(ob))
1155 myname = name();
1156 else
1157 myname="("+
1158 ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
1159 capitalize(getuid()) + ")";
1160 }
1161 else
1162 myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
Bugfix45f88ce2017-03-06 14:41:56 +01001163 QueryProp(P_PRESAY):"") + capitalize(getuid(ME));
MG Mud User88f12472016-06-24 23:31:02 +02001164 if (myname && sizeof(myname)) myname=capitalize(myname);
1165 // erstmal an Empfaenger senden
1166 _send(ob, permutate(msg), MT_COMM|MT_FAR, MA_TELL,
1167 myname + " teilt Dir mit: ");
1168
1169 // dann evtl. noch an Absender ausgeben...
1170 if (visflag = !ob->QueryProp(P_INVIS) || IS_LEARNER(this_player()))
1171 _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(it) + " mit: ");
1172 // oder irgendwas anderes an den Absender ausgeben...
1173 if (!visflag && interactive(ob))
1174 _notify("Kein solcher Spieler!",MA_TELL);
Zesstra04f613c2019-11-27 23:32:54 +01001175 else if (away = ({string})ob->QueryProp(P_AWAY))
MG Mud User88f12472016-06-24 23:31:02 +02001176 ReceiveMsg( break_string( away, 78, capitalize(it)
1177 + " ist gerade nicht da: ", BS_INDENT_ONCE ),
1178 MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
1179 MA_TELL, 0, this_object());
1180 else if (interactive(ob) && (i=query_idle(ob))>=600)
1181 { //ab 10 Mins
1182 if (i<3600)
1183 away=time2string("%m %M",i);
1184 else
1185 away=time2string("%h %H und %m %M",i);
1186
1187 _notify(sprintf("%s ist seit %s voellig untaetig.",
1188 capitalize(it),away),
1189 MA_TELL);
1190 }
1191
1192 return 1;
1193}
1194
1195static int _teile(string str)
1196{
1197 string who, message;
1198 if (!(str=_unparsed_args())) return 0;
1199 if (sscanf(str, "%s mit %s", who, message) == 2)
1200 return _tell(who, message,1);
1201 return 0;
1202}
1203static int _teile_mit_alias(string str)
1204{
1205 str = _unparsed_args(), TRIM_LEFT;
1206 if (!str) return 0;
1207 str = trim(str, TRIM_LEFT);
1208 // Ziel muss min. 2 Buchstaben haben (.<nr>)
1209 if (sizeof(str) < 4) return 0;
1210 int pos = strstr(str, " ");
1211 if (pos >= 2)
1212 return _tell(str[..pos-1], str[pos+1..]);
1213 return 0;
1214}
1215
1216static int _erzaehle(string str)
1217{
1218 string who, message;
1219
1220 if (!(str=_unparsed_args())) return 0;
1221 if (sscanf(str, "%s %s", who, message) == 2)
1222 return _tell(who, message,1);
1223 return 0;
1224}
1225
1226static int _whisper(string str)
1227{
1228 object ob;
1229 string who;
1230 string msg;
1231 string myname;
1232
1233 if (!(str=_unparsed_args()) ||
1234 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1235 sscanf(str, "%s %s", who, msg) !=2 )) {
1236 _notify("Was willst Du wem zufluestern?",MA_SAY);
1237 return 1;
1238 }
Zesstra6e88b6a2019-11-08 00:25:39 +01001239 who = lower_case(who);
MG Mud User88f12472016-06-24 23:31:02 +02001240 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>";
Zesstraa31cd5c2016-12-17 20:11:07 +01001866 add_to_tell_history(uid, 0, 1, msg, msg_prefix,
1867 (msg_action == MA_TELL ? MSGFLAG_TELL : 0 ) );
MG Mud User88f12472016-06-24 23:31:02 +02001868 }
1869
1870 // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
1871 // Ebenenmeldungen. (Die haben ggf. schon.)
1872 if (stringp(msg_action) && QueryProp(P_AWAY)
1873 && strstr(msg_action, MA_CHANNEL) != 0)
1874 {
1875 // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
1876 if (msg[<1] == '\n')
1877 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1878 else
1879 msg = msg + " [" + strftime("%H:%M",time()) + "]";
1880 }
1881 // Kobold erlaubt und gewuenscht? Kobold ist fuer die
1882 // direkte Kommunikation mittels MT_COMM vorgesehen.
1883 // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
1884 // ignorieren.
1885 // die if-Konstruktion ist so, weil ich das _flush_cache() im else
1886 // brauche.
1887 if (query_editing(this_object()) || query_input_pending(this_object())
1888 || QueryProp(P_EARMUFFS))
1889 {
1890 if (!(flags & MSG_DONT_BUFFER)
1891 && QueryProp(P_BUFFER))
1892 {
1893 // Nachricht soll im Kobold gespeichert werden.
1894 return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
1895 }
1896 }
1897 else
1898 {
1899 // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
1900 // entleeren.
1901 _flush_cache(0);
1902 }
1903
1904 // ggf. Piepston anhaengen. NACH Koboldablage, die sollen erstmal keinen
1905 // Pieps kriegen.
1906 if (comm_beep())
1907 msg=msg + MESSAGE_BEEP;
1908 }
1909
1910 // Ausgabenachricht bauen und an den Spieler senden.
1911 if (flags & MSG_DONT_WRAP)
1912 msg = (msg_prefix ? msg_prefix : "") + msg;
1913 else
1914 {
1915 int bsflags = flags & MSG_ALL_BS_FLAGS;
1916 if (QueryProp(P_MESSAGE_PREPEND))
1917 bsflags |= BS_PREPEND_INDENT;
1918 msg = break_string(msg, 78, msg_prefix, bsflags);
1919 }
1920 efun::tell_object(ME, msg);
1921
1922 return MSG_DELIVERED;
1923}