blob: 7025a860fd1abda616dc4e70eae0021c77676560 [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>
Zesstra3a261e52022-02-10 14:00:31 +010040#include <ansi.h>
41#include <assert.h>
MG Mud User88f12472016-06-24 23:31:02 +020042
43#define TELLHIST_DISABLED 0
44#define TELLHIST_NO_MESSAGE 1
45#define TELLHIST_ENABLED 2
46#define TELLHIST_LONGLIFE 3
47
48#define ECHO_COST 50
49#define ERWIDER_PARAM ","
50
51#define ZDEBUG(x) if (find_player("zesstra"))\
52 efun::tell_object(find_player("zesstra"),"CommDBG: "+x+"\n")
53
54private int tell_history_enabled = TELLHIST_NO_MESSAGE;
55private nosave mapping tell_history=([]);
56private nosave string *commreceivers = ({});
57private nosave string last_comm_partner;
58private nosave int last_beep_time;
59
60// Statusreporte aktiviert? Binaere Flags (s. set_report())
61private int stat_reports;
62// interner Cache fuer die LP/KP/Gift-Werte fuer die Statusreport-Ausgaben
63// Eintraege (in dieser Reihenfolge): P_HP, P_SP, Giftstatus
64// Initialisierung erfolgt beim ersten Report nach Login
65private nosave mixed *report_cache;
66
67// Puffer fuer Kobold.
68private nosave struct msg_buffer_s kobold = (<msg_buffer_s>
69 buf: allocate(32),
70 index: -1,);
71#define MAX_KOBOLD_LIMIT 256
72
Zesstra3a261e52022-02-10 14:00:31 +010073// Colourmap
74// TODO: spaeter konfigurierbar machen
75private mapping build_colourmap(string ttype="ansi");
76private nosave mapping colourmap = build_colourmap();
77
MG Mud User88f12472016-06-24 23:31:02 +020078varargs string name(int casus, int demonst);
79
80//local property prototypes
81static int _query_intermud();
82public int RemoveIgnore(string ign);
83public int AddIgnore(string ign);
84
85public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
86 string msg_prefix, object origin);
87
88// erzeugt sortierte Liste an Kommunikationspartnern
89private string *sorted_commpartners(int reversed);
90
MG Mud User88f12472016-06-24 23:31:02 +020091void create()
92{
93 ::create();
Bugfix3afcb792022-01-21 22:32:42 +010094 Set(P_ALERT, SAVE, F_MODE_AS);
MG Mud User88f12472016-06-24 23:31:02 +020095 Set(P_EARMUFFS, 0);
96 Set(P_EARMUFFS, SAVE, F_MODE);
97 Set(P_EARMUFFS, SECURED, F_MODE);
98 Set(P_INTERMUD, SAVE, F_MODE);
99 Set(P_IGNORE, ([]), F_VALUE);
100 Set(P_IGNORE, SAVE, F_MODE);
101 Set(P_BUFFER, SAVE, F_MODE);
102 Set(P_MESSAGE_PREPEND, SAVE, F_MODE_AS);
103 Set(P_MESSAGE_BEEP, SAVE, F_MODE_AS);
104}
105
106void create_super()
107{
108 set_next_reset(-1);
109}
110
Zesstra3a261e52022-02-10 14:00:31 +0100111private mapping build_colourmap(string ttype)
112{
113 mapping res = ([0:""]);
114 switch(ttype)
115 {
116 case "dumb":
117 return res;
118 case "ansi":
119 res = ([ 0:ANSI_NORMAL, "normal": ANSI_NORMAL,
120 "bold": ANSI_BOLD, "underlined": ANSI_UNDERL,
121 "blink": ANSI_BLINK, "invers": ANSI_INVERS,
122 "black": ANSI_BLACK, "red": ANSI_RED,
123 "green": ANSI_GREEN, "yellow": ANSI_YELLOW,
124 "blue": ANSI_BLUE, "purple": ANSI_PURPLE,
125 "cyan": ANSI_CYAN, "white": ANSI_WHITE,
126 "bg_black": ANSI_BG_BLACK, "bg_red": ANSI_BG_RED,
127 "bg_green": ANSI_BG_GREEN, "bg_yellow": ANSI_BG_YELLOW,
128 "bg_blue": ANSI_BG_BLUE, "bg_purple": ANSI_BG_PURPLE,
129 "bg_cyan": ANSI_BG_CYAN, "bg_white": ANSI_BG_WHITE,
130 "mention": ANSI_BOLD+ANSI_BG_BLUE,
131 ]);
132 break;
133 case "vt100":
134 res += ([0:ANSI_NORMAL, "normal": ANSI_NORMAL,
135 "bold": ANSI_BOLD, "underlined": ANSI_UNDERL,
136 "blink": ANSI_BLINK, "invers": ANSI_INVERS,
137 "mention": ANSI_BOLD,
138 ]);
139 break;
140 default:
141 assert(1, "Ungueltiger Terminaltyp in build_colourmap");
142 }
143 return res;
144}
145
146protected void set_colourmap(string ttype="ansi")
147{
148 colourmap = build_colourmap(ttype);
149}
150
Zesstra0681c732020-10-31 19:45:47 +0100151// called from base.c in Reconnect()
152protected void reconnect() {
153 // Cache fuer den report zuruecksetzen, der koennte veraltet sein (insb.
154 // falls in der letzten Session GMCP benutzt wurde und jetzt nicht).
155 report_cache = 0;
156}
157
MG Mud User88f12472016-06-24 23:31:02 +0200158protected void updates_after_restore(int newflag) {
Zesstra3a261e52022-02-10 14:00:31 +0100159 // Colourmap aktualisieren nach Restore
160 colourmap = build_colourmap(QueryProp(P_TTY));
161
MG Mud User88f12472016-06-24 23:31:02 +0200162 // Altes Ignoriere loeschen...
163 mixed ign = Query(P_IGNORE,F_VALUE);
164 if (!mappingp(ign))
165 {
166 if (pointerp(ign))
Bugfixb1d9b4d2021-09-14 20:07:04 +0200167 ReceiveNotify(break_string(
MG Mud User88f12472016-06-24 23:31:02 +0200168 "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
169 "weil es eine Aktualisierung der Ignorierefunktion gab, "
170 "bei der eine Konversion der Daten leider nicht "
171 "moeglich war.",78), 0);
172
173 Set(P_IGNORE, ([]), F_VALUE);
174 }
175}
176
Zesstra2504b2d2020-05-22 12:30:17 +0200177static int set_report(string str)
178{
MG Mud User88f12472016-06-24 23:31:02 +0200179 int canflags = QueryProp(P_CAN_FLAGS);
MG Mud User88f12472016-06-24 23:31:02 +0200180 if(!str)
181 {
Zesstracf8f2952020-05-22 12:07:52 +0200182 if (stat_reports)
183 {
184 string *res=({});
185 if (stat_reports & DO_REPORT_HP)
186 res+=({"Lebenspunkte"});
187 if (stat_reports & DO_REPORT_SP)
188 res+=({"Konzentrationspunkte"});
189 if (stat_reports & DO_REPORT_POISON)
190 res+=({"Vergiftungen"});
191 if (stat_reports & DO_REPORT_WIMPY)
192 res+=({"Vorsicht"});
MG Mud User88f12472016-06-24 23:31:02 +0200193
Zesstracf8f2952020-05-22 12:07:52 +0200194 tell_object(ME,break_string(
MG Mud User88f12472016-06-24 23:31:02 +0200195 "Dir werden jetzt Veraenderungen Deiner "
196 +CountUp(res) + " berichtet.",78));
Zesstra86ec63b2020-05-22 12:09:56 +0200197 if (GMCP_Status("MG.char") || GMCP_Status("char")
198 || GMCP_Status("Char"))
199 {
200 tell_object(ME,break_string(
201 "Achtung: Dein Client laesst sich den Report per GMCP "
Zesstra2504b2d2020-05-22 12:30:17 +0200202 "(s. 'hilfe GMCP') uebermitteln. Daher wird er Dir nicht "
Zesstra86ec63b2020-05-22 12:09:56 +0200203 "in der Textausgabe des Spiels angezeigt! Moechtest Du "
204 "dies nicht, schalte bitte in Deinem Client GMCP-Module mit "
205 "Namen wie 'MG.char', 'char', 'Char' oder aehnliche aus."));
206 }
MG Mud User88f12472016-06-24 23:31:02 +0200207 }
208 else
209 tell_object(ME,
210 "Alle Statusreports sind ausgeschaltet.\n");
211
212 return 1;
213 }
Zesstra2504b2d2020-05-22 12:30:17 +0200214 else if (str == "aus")
215 {
216 if (stat_reports & DO_REPORT_HP || stat_reports & DO_REPORT_WIMPY)
217 {
MG Mud User88f12472016-06-24 23:31:02 +0200218 string s="";
Zesstra2504b2d2020-05-22 12:30:17 +0200219 if (stat_reports & DO_REPORT_HP)
220 {
MG Mud User88f12472016-06-24 23:31:02 +0200221 str="ebenfalls ";
222 tell_object(ME, "Der Report wurde ausgeschaltet.\n");
223 }
Zesstra2504b2d2020-05-22 12:30:17 +0200224 if ( stat_reports & DO_REPORT_WIMPY )
225 {
MG Mud User88f12472016-06-24 23:31:02 +0200226 tell_object(ME, "Der Vorsicht-Report wurde "+s+
227 "ausgeschaltet.\n");
228 }
229 stat_reports=0;
230 }
Zesstra2504b2d2020-05-22 12:30:17 +0200231 else
232 {
MG Mud User88f12472016-06-24 23:31:02 +0200233 tell_object(ME, "Der Report ist bereits ausgeschaltet.\n");
234 }
235 return 1;
236 }
Zesstra2504b2d2020-05-22 12:30:17 +0200237 else if (str == "ein")
238 {
239 if ( stat_reports & DO_REPORT_HP )
240 {
MG Mud User88f12472016-06-24 23:31:02 +0200241 tell_object(ME, "Der Report ist bereits eingeschaltet.\n");
242 return 1;
243 }
244 tell_object(ME, "Der Report wurde eingeschaltet.\n");
245 stat_reports |= DO_REPORT_HP;
Zesstra2504b2d2020-05-22 12:30:17 +0200246 if (!(canflags & CAN_REPORT_SP))
247 {
248 if (QueryQuest("Hilf den Gnarfen")==1)
249 {
MG Mud User88f12472016-06-24 23:31:02 +0200250 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_SP);
251 stat_reports |= DO_REPORT_SP;
252 }
Zesstra2504b2d2020-05-22 12:30:17 +0200253 else
254 {
MG Mud User88f12472016-06-24 23:31:02 +0200255 tell_object(ME, break_string(
256 "Fuer den Statusreport Deiner Konzentration musst Du jedoch "
257 "zunaechst die Quest \"Hilf den Gnarfen\" bestehen.",78));
258 }
259 }
Zesstra2504b2d2020-05-22 12:30:17 +0200260 else
261 {
MG Mud User88f12472016-06-24 23:31:02 +0200262 stat_reports |= DO_REPORT_SP;
263 }
Zesstra2504b2d2020-05-22 12:30:17 +0200264 if (!(canflags & CAN_REPORT_POISON))
265 {
266 if (QueryQuest("Katzenjammer")==1)
267 {
MG Mud User88f12472016-06-24 23:31:02 +0200268 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_POISON);
269 stat_reports |= DO_REPORT_POISON;
270 }
Zesstra2504b2d2020-05-22 12:30:17 +0200271 else
272 {
MG Mud User88f12472016-06-24 23:31:02 +0200273 tell_object(ME, break_string(
274 "Fuer den Statusreport Deiner Vergiftung musst Du jedoch "
275 "zunaechst die Quest \"Katzenjammer\" bestehen.",78));
276 }
277 }
Zesstra2504b2d2020-05-22 12:30:17 +0200278 else
279 {
MG Mud User88f12472016-06-24 23:31:02 +0200280 stat_reports |= DO_REPORT_POISON;
281 }
282 // Cache loeschen, damit beim naechsten Report-Event alle Daten neu
283 // eingetragen werden muessen. Muss beim Einschalten des Reports
284 // passieren, weil auch in der inaktiven Zeit weiterhin Aenderungen in
285 // status_report() eingehen, so dass der Cache zwar erst einmal leer ist,
286 // aber beim Wiedereinschalten nicht mehr ungueltig waere und somit
287 // veraltete Daten an den Spieler ausgegeben werden. Im unguenstigsten
288 // Fall wuerde das sogar dazu fuehren, dass die veralteten Daten lange
289 // Zeit nicht aktualisiert werden, wenn z.B. P_HP == P_MAX_HP, so dass
290 // kein P_HP-Event mehr eingeht.
291 report_cache=0;
Zesstra2504b2d2020-05-22 12:30:17 +0200292 // Fall-through fuer Statusausgabe
MG Mud User88f12472016-06-24 23:31:02 +0200293 }
Zesstra2504b2d2020-05-22 12:30:17 +0200294 else if (str == "vorsicht")
295 {
296 if (!(canflags & CAN_REPORT_WIMPY))
297 {
298 if (QueryQuest("Schrat kann nicht einschlafen")==1)
299 {
MG Mud User88f12472016-06-24 23:31:02 +0200300 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_WIMPY);
301 tell_object(ME, "Der Vorsicht-Report wurde eingeschaltet.\n");
302 stat_reports |= DO_REPORT_WIMPY;
303 }
Zesstra2504b2d2020-05-22 12:30:17 +0200304 else
305 {
MG Mud User88f12472016-06-24 23:31:02 +0200306 tell_object(ME, break_string(
307 "Fuer den Statusreport Deiner Vorsicht musst Du "
308 "zunaechst die Quest \"Schrat kann nicht einschlafen\" "
309 "bestehen.",78));
310 }
311 }
312 else
313 {
314 stat_reports |= DO_REPORT_WIMPY;
315 }
316 // fuer Seher auch Bericht der Fluchtrichtung einschalten.
317 if ((stat_reports & DO_REPORT_WIMPY)
318 && !(stat_reports & DO_REPORT_WIMPY_DIR)
319 && ((canflags & CAN_REPORT_WIMPY) || IS_SEER(ME)))
320 {
Zesstra2504b2d2020-05-22 12:30:17 +0200321 stat_reports |= DO_REPORT_WIMPY_DIR;
MG Mud User88f12472016-06-24 23:31:02 +0200322 }
Zesstra2504b2d2020-05-22 12:30:17 +0200323 // Fall-through fuer Statusausgabe
MG Mud User88f12472016-06-24 23:31:02 +0200324 }
325 // sendet einmalig genau jetzt den konfigurierten report. Kann zum testen
326 // (von Triggern) oder beim Login benutzt werden, wenn man einen initialen
327 // Datenbestand erhalten will.
328 else if (str=="senden")
329 {
330 // Es wird Ausgabe von LP und Vorsicht getriggert, das sendet beide
331 // Zeilen.
332 status_report(DO_REPORT_HP, QueryProp(P_HP));
333 status_report(DO_REPORT_WIMPY, QueryProp(P_WIMPY));
334 return 1;
335 }
336 else
337 return 0;
338 // nur aktuellen Zustand berichten
339 set_report(0);
340 return 1;
341}
342
343private string get_poison_desc(int p) {
344 string ret;
345 if ( intp(p) ) {
346 switch(p) {
347 case 0: ret="keins"; break;
348 case 1..3: ret="leicht"; break;
349 case 4..8: ret="gefaehrlich"; break;
350 default: ret="sehr ernst"; break;
351 }
352 return ret;
353 }
354 else return "(nicht verfuegbar)";
355}
356
357// sprintf()-Formatstrings fuer die Reportausgabe.
358#define REPORTLINE "LP: %3d, KP: %3s, Gift: %s.\n"
359#define REPORTLINE_WIMPY "Vorsicht: %d, Fluchtrichtung: %s.\n"
360// Defines zur Adressierung der Cache-Eintraege
361#define REP_HP 0
362#define REP_SP 1
363#define REP_POISON 2
364
365protected void status_report(int type, mixed val) {
366 // Wenn der Spieler GMCP hat und das sich um die Information kuemmert,
367 // erfolgt keine textuelle Ausgabe mehr. Daher return, wenn GMCP_Char()
368 // erfolg vermeldet hat.
369 int flags = QueryProp(P_CAN_FLAGS);
370 switch (type) {
371 case DO_REPORT_HP:
372 if (GMCP_Char( ([ P_HP: val ]) ) ) return;
373 break;
374 case DO_REPORT_SP:
375 if (!(flags & CAN_REPORT_SP)) return;
376 if (GMCP_Char( ([ P_SP: val ]) ) ) return;
377 break;
378 case DO_REPORT_POISON:
379 if (!(flags & CAN_REPORT_POISON)) return;
380 if (GMCP_Char( ([ P_POISON: val ]) ) ) return;
381 break;
382 case DO_REPORT_WIMPY:
383 if (!(flags & CAN_REPORT_WIMPY)) return;
384 if (GMCP_Char( ([ P_WIMPY: val ]) ) ) return;
385 break;
386 case DO_REPORT_WIMPY_DIR:
387 if (!(flags & CAN_REPORT_WIMPY_DIR)) return;
388 if (GMCP_Char( ([ P_WIMPY_DIRECTION: val ]) ) ) return;
389 break;
390 }
391
392 // konventionelle textuelle Ausgabe des Reports ab hier.
393 if (!(type & stat_reports))
394 return;
395
396 if ( !report_cache ) {
397 report_cache = ({
398 QueryProp(P_HP),
399 (stat_reports&DO_REPORT_SP) ? to_string(QueryProp(P_SP)) : "###",
400 (stat_reports&DO_REPORT_POISON) ?
401 get_poison_desc(QueryProp(P_POISON)) : "(nicht verfuegbar)"
402 });
403 }
404
405 switch(type) {
406 // LP berichten: Cache aktualisieren und Meldung ausgeben.
407 case DO_REPORT_HP:
408 report_cache[REP_HP]=val;
409 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
410 report_cache[REP_SP], report_cache[REP_POISON]));
411 break;
412 // KP berichten: Wenn der Spieler den Report freigeschaltet hat,
413 // wird bei Aenderungen gemeldet. Wenn nicht, aendert sich nur der
414 // Cache-Eintrag. So wird verhindert, dass ein Spieler ueber KP-
415 // Veraenderungen auch dann informiert wuerde, wenn er den KP-Report
416 // gar nicht benutzen koennte.
417 case DO_REPORT_SP:
418 report_cache[REP_SP]=to_string(val);
419 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
420 report_cache[REP_SP], report_cache[REP_POISON]));
421 break;
422 // Giftstatus berichten: Wenn der Giftreport freigeschaltet ist,
423 // Cache aktualisieren und berichten. Wenn nicht, aendert sich nur
424 // der Cache-Eintrag. Erlaeuterung hierzu s.o. beim KP-Report.
425 case DO_REPORT_POISON:
426 report_cache[REP_POISON] = get_poison_desc(val);
427 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
428 report_cache[REP_SP], report_cache[REP_POISON]));
429 break;
430 // Vorsicht-Report: kann ohne weitere Abfragen ausgegeben werden, da
431 // alle noetigen Checks schon zu Beginn dieser Funktion erledigt wurden.
432 // Lediglich der Inhalt der Meldung muss abhaengig vom Seherstatus
433 // konfiguriert werden.
434 case DO_REPORT_WIMPY:
435 string res;
436 if (IS_SEER(ME)) {
437 // QueryProp() aus Kostengruenden im if(), damit die Aufruf-
438 // Haeufigkeit zumindest ein wenig reduziert wird.
439 string dir = QueryProp(P_WIMPY_DIRECTION)||"keine";
440 res = sprintf(REPORTLINE_WIMPY, val, dir);
441 }
442 else
443 res = sprintf(REPORTLINE_WIMPY, val, "(nicht verfuegbar)");
444 tell_object(ME, res);
445 break;
446 // Fluchtrichtungs-Report: wird nur bei Sehern ausgegeben, damit
447 // nicht auch Spieler eine VS-/FR-Meldung bekommen, wenn z.B. eine
448 // externe Manipulation der Fluchtrichtung stattfindet, sie aber den
449 // Report mangels Seherstatus gar nicht freigeschaltet haben.
450 case DO_REPORT_WIMPY_DIR:
451 if (IS_SEER(ME)) {
452 if (!val) val = "keine";
453 tell_object(ME,sprintf(REPORTLINE_WIMPY, QueryProp(P_WIMPY), val));
454 }
455 break;
456 }
457}
458
459#undef REPORTLINE
460#undef REPORTLINE_WIMPY
461#undef REP_HP
462#undef REP_SP
463#undef REP_POISON
464
465private string permutate(string msg)
466{
467 // Kontrollzeichen rausfiltern. *seufz*
468 msg = regreplace(msg,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
469 object ob=QueryProp(P_PERM_STRING);
470 if (!objectp(ob))
471 return msg;
472
Zesstra04f613c2019-11-27 23:32:54 +0100473 return ({string})ob->permutate_string(msg)||"";
MG Mud User88f12472016-06-24 23:31:02 +0200474}
475
476// neue nachricht an den Kobold anhaengen
477// Rueckgabewerte: MSG_BUFFER_FULL oder MSG_BUFFERED
478private int add_to_kobold(string msg, int msg_type, string msg_action,
479 string msg_prefix, object origin)
480{
481 // Nachricht soll im Kobold gespeichert werden.
482 // Kobold speichert Rohdaten und gibt spaeter das ganze auch wieder via
483 // ReceiveMsg() aus - dabei wird MSG_DONT_BUFFER | MSG_DONT_STORE gesetz,
484 // damit keine erneute Speicher in Kobold oder Komm-History erfolgt.
485
486 // wenn der Puffer zu klein ist, Groesse verdoppeln, wenn noch unterhalb
487 // des Limits.
488 if (kobold->index >= sizeof(kobold->buf)-1) {
489 if (sizeof(kobold->buf) < MAX_KOBOLD_LIMIT)
490 kobold->buf += allocate(sizeof(kobold->buf));
491 else
492 return MSG_BUFFER_FULL;
493 }
494 kobold->index = kobold->index +1;
495 // neue Nachricht an den Puffer anhaengen.
496 string sendername = query_once_interactive(origin) ?
497 origin->query_real_name() :
498 origin->name(WER) || "<Unbekannt>";
Zesstraa5fda4a2022-01-06 17:31:44 +0100499 kobold->buf[kobold->index] = (<kobold_msg_s> msg: msg,
MG Mud User88f12472016-06-24 23:31:02 +0200500 type : msg_type, action : msg_action, prefix : msg_prefix,
501 sendername : sendername);
502 return MSG_BUFFERED;
503}
504
505private void _flush_cache(int verbose) {
506 // nur mit genug Evalticks ausgeben.
507 if (get_eval_cost() < 100000) return;
508 if (kobold->index >= 0)
509 {
510 ReceiveMsg("Ein kleiner Kobold teilt Dir folgendes mit:",
511 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
512 0, 0, this_object());
513 int prepend = QueryProp(P_MESSAGE_PREPEND);
514 foreach(int i: 0 .. kobold->index) // '0 ..' ist wichtig!
515 {
Zesstraa5fda4a2022-01-06 17:31:44 +0100516 struct kobold_msg_s msg = kobold->buf[i];
MG Mud User88f12472016-06-24 23:31:02 +0200517 // dies ist dient der Fehlerabsicherung, falls es nen Fehler (z.B. TLE)
518 // in der Schleife unten gab: dann ist index nicht auf -1 gesetzt
519 // worden, aber einige Nachrichten sind schon geloescht.
520 if (!structp(msg)) continue;
521 // Ausgabe via efun::tell_object(), weil die Arbeit von ReceiveMsg()
Zesstra3a261e52022-02-10 14:00:31 +0100522 // schon getan wurde. Allerdings muessen wir uns noch um den Umbruch
523 // und Farben kuemmern.
524 msg->msg = terminal_colour(msg->msg, colourmap);
MG Mud User88f12472016-06-24 23:31:02 +0200525 if ((msg->type) & MSG_DONT_WRAP)
526 msg->msg = (msg->prefix ? msg->prefix : "") + msg->msg;
527 else
528 {
529 int bsflags = msg->type & MSG_ALL_BS_FLAGS;
530 if (prepend)
531 bsflags |= BS_PREPEND_INDENT;
532 msg->msg = break_string(msg->msg, 78, msg->prefix, bsflags);
533 }
534 efun::tell_object(this_object(), msg->msg);
535 kobold->buf[i]=0;
536 }
537 kobold->index=-1;
538 }
539 else if (verbose)
540 {
541 ReceiveMsg("Der kleine Kobold hat leider nichts Neues fuer Dich.",
542 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
543 0, 0, this_object());
544 }
545}
546
547varargs int cmd_kobold(string arg)
548{
549 switch(arg)
550 {
551 case "ein":
552 SetProp(P_BUFFER, 1);
553 printf("Der Kobold merkt sich jetzt alles!\n"); break;
554 case "aus":
555 SetProp(P_BUFFER, 0);
556 printf("Der Kobold wird Dich nicht stoeren!\n"); break;
557 default: if(arg) printf("Der Kobold sagt: kobold ein oder kobold aus\n");
558 }
559 _flush_cache(1);
560 return 1;
561}
562
563public int TestIgnoreSimple(string *arg)
Zesstrade642d22019-11-23 17:22:34 +0100564{ mapping ignore;
MG Mud User88f12472016-06-24 23:31:02 +0200565
566 if (!pointerp(arg) || !mappingp(ignore=Query(P_IGNORE,F_VALUE)))
567 return 0;
568 foreach(string s: arg)
569 {
570 if (member(ignore,s))
571 return 1;
572 }
573 return 0;
574}
575
576//TODO: deprecated - entfernen, wenn Message() entfernt wird.
577private int check_ignore(mixed ignore, string verb, string name)
578{
579 if (ignore == verb)
580 return 1;
581 ignore = explode(ignore, ".");
582 return ((sizeof(ignore) > 1) &&
583 (name == ignore[0] && member(ignore[1..], verb) != -1));
584}
585
Zesstra3a261e52022-02-10 14:00:31 +0100586// Die Nachricht kommt mit einem Alert oder Mention. comm_beep() prueft, ob
587// ein Piepston ausgegeben werden soll (langfristig andere Benachrichtigungen
588// geplant!).
589// Ueblicherweise werden nur alle <P_MESSAGE_BEEP> Sekunden Toene
590// uebermittelt.
591private void comm_beep(string msg_action, int mention=0)
Bugfix60f5bc62022-01-21 11:09:56 +0100592{
Zesstra1cc6cf32022-02-09 11:49:31 +0100593 // Wenn Alerts komplett abgeschaltet sind, gibts nix.
594 int alert_config = ({int})QueryProp(P_ALERT);
595 if (alert_config & AL_NO_SOUND)
596 return; // kein ton
597 // und maximal alle P_MESSAGE_BEEP Sekunden aufmerksam machen; bei 0s ist
598 // jedes Mal erlaubt.
Zesstra04f613c2019-11-27 23:32:54 +0100599 int beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
Zesstra1cc6cf32022-02-09 11:49:31 +0100600 if ((time()-last_beep_time) < beep_interval)
601 return;
602 int required;
603 switch(msg_action)
Bugfix60f5bc62022-01-21 11:09:56 +0100604 {
Zesstra1cc6cf32022-02-09 11:49:31 +0100605 case MA_TELL:
606 required |= MB_TELL;
607 break;
608 case MA_SAY:
609 required |= MB_SAY;
610 break;
611 case MA_CHANNEL:
612 required |= MB_CHANNEL;
613 break;
614 case MA_SHOUT:
615 required |= MB_SHOUT;
616 break;
617 default:
618 // Alle anderen Aktionen, die keine gesonderten Aktionsfilter haben
619 required |= MB_MISC;
620 break;
Bugfix60f5bc62022-01-21 11:09:56 +0100621 }
Zesstra3a261e52022-02-10 14:00:31 +0100622 if (mention)
623 required |= MB_MENTION;
624
Zesstra1cc6cf32022-02-09 11:49:31 +0100625 // wenn in alert min. ein notwendiges Flag drin ist darf gepiepst werden
626 if (required & alert_config)
627 {
628 last_beep_time = time();
629 binary_message(b"\a", 1);
630 }
MG Mud User88f12472016-06-24 23:31:02 +0200631}
632
633private varargs void add_to_tell_history( string uid, int sent, int recv,
634 string message, string indent, int flags )
635{
636 /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
637 Als Wert ist eine Strukur vom Typ chat_s eingetragen.
638 Strukturen chat_s und stored_msg_s sind in /std/player/comm_structs.c
639 definiert.
640 TODO fuer spaeter, gerade keine Zeit fuer:
641 Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
642 jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
643 weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
644 Element ist aeltestes Gespraech).
645 */
646
647 //TODO: Entfernen, wenn das nicht mehr passiert.
648 if (!stringp(uid))
649 {
650 ReceiveMsg(sprintf(
651 "\nadd_to_tell_history(): got bad uid argument %O."
652 "sent: %d, recv: %d, flags: %d, msg: %s",
653 uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);
654 }
Zesstraa31cd5c2016-12-17 20:11:07 +0100655
MG Mud User88f12472016-06-24 23:31:02 +0200656 // letzten Gespraechspartner fuer erwidere.
Zesstraa31cd5c2016-12-17 20:11:07 +0100657 if (flags & (MSGFLAG_TELL|MSGFLAG_RTELL))
MG Mud User88f12472016-06-24 23:31:02 +0200658 last_comm_partner = uid;
659
660 // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
661 // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
662 // Anfang.
663 if (sent) {
664 if (!sizeof(commreceivers))
665 commreceivers = ({uid});
666 else if (commreceivers[0] != uid) {
667 // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
668 // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
669 // entfernen.
670 // TODO: Effizienter gestalten.
671 commreceivers = ({uid}) + (commreceivers-({uid}));
672 // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
673 // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
674 // commreceivers drin sind.)
675 if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
676 commreceivers = commreceivers[0..MAX_SAVED_CHATS];
677 }
678 }
679
680 if (!tell_history_enabled)
681 return;
682
683 if (!indent && message[<1] == 10)
684 message = message[..<2];
685
686 struct chat_s chat;
687 // Gespraechspartner unbekannt?
688 if (!member(tell_history, uid)) {
689 // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
690 // dazu kommt.
691 if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
692 string deluid;
693 int zeit = __INT_MAX__;
694 foreach(string tuid, chat : tell_history) {
695 // aeltestes Gespraech suchen
696 if (zeit > chat->time_last_msg) {
697 deluid = tuid;
698 zeit = chat->time_last_msg;
699 }
700 }
701 // aeltestes Gespraech raus.
702 m_delete(tell_history, deluid);
703 if (member(commreceivers,deluid)>-1)
704 commreceivers-=({deluid});
705 }
706 // neues Gespraech anlegen
707 chat = (<chat_s> uid: uid, time_first_msg: time(),
708 time_last_msg: time(),
709 sentcount: sent, recvcount: recv,
710 msgbuf: 0, ptr: 0 );
711 tell_history[uid] = chat;
712 }
713 else {
714 // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
715 chat = tell_history[uid];
716 chat->time_last_msg = time();
717 chat->sentcount += sent;
718 chat->recvcount += recv;
719 }
720
721 if (tell_history_enabled < TELLHIST_ENABLED)
722 return;
723
724 // ggf. Array fuer Messages anlegen
725 if (!pointerp(chat->msgbuf))
726 chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
727
728 // Message-Struktur ermitteln oder neu anlegen
729 struct stored_msg_s msg;
730 if (!structp(chat->msgbuf[chat->ptr])) {
731 // neue Struct ins Array schreiben
732 chat->msgbuf[chat->ptr] = msg = (<stored_msg_s>);
733 }
734 else {
735 // alte Struct ueberschreiben
736 msg = chat->msgbuf[chat->ptr];
737 }
738 // Index auf naechste Messagestruktur ermitteln
739 chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
740 // Message speichern
741 msg->msg = message;
742 msg->prefix = indent;
743 msg->timestamp = time();
744}
745
746protected void clear_tell_history()
747{
748 /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
749 sofern der Spieler nichts abweichendes eingestellt hat. */
750
751#ifdef TELLHIST_LONGLIFE
752 if (tell_history_enabled == TELLHIST_LONGLIFE)
753 return;
754#endif
755
756 foreach (string uid, struct chat_s chat: tell_history)
757 if (pointerp(chat->msgbuf)) {
758 chat->msgbuf = 0;
759 chat->ptr = 0;
760 }
761}
762
763protected void reset(void)
764{
765 /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
766 Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
767 wie bei einem "schlafe ein" die Mitteilungen geloescht. */
768
769 if (!interactive())
770 clear_tell_history();
771
772}
773
774// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
775// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
776// werden soll.
777private void _send(object ob, string msg, int msg_type,
778 string msg_action, string msg_prefix)
779{
780 int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
781 switch(res) {
782 case MSG_DELIVERED:
783 break; // nix machen
784 case MSG_BUFFERED:
Zesstra62b4a862022-12-23 20:08:16 +0100785 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200786 "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
787 "genommen. Er wird sie spaeter weiterleiten!",
788 MT_NOTIFICATION, msg_action, 0, this_object());
789 break;
790 case MSG_IGNORED:
791 case MSG_VERB_IGN:
792 case MSG_MUD_IGN:
793 ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
794 MT_NOTIFICATION, msg_action, 0, this_object());
795 break;
796 case MSG_SENSE_BLOCK:
797 ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
798 MT_NOTIFICATION, msg_action, 0, this_object());
799 break;
800 case MSG_BUFFER_FULL:
Zesstra62b4a862022-12-23 20:08:16 +0100801 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200802 "Die Mitteilung ging verloren, denn der Kobold kann sich "
803 "nichts mehr merken!", MT_NOTIFICATION, msg_action,
804 0, this_object());
805 break;
806 default:
807 ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
808 "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
809 break;
810 }
811}
812
813// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
814// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
815// die Nachricht ggf. in den Kommhistory erfasst wird.
816// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
817protected varargs int _recv(object ob, string message, int flag, string indent)
818{
819 write(break_string(message, 78, indent,
820 QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
821 if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
822 query_once_interactive(ob))
823 {
824 if (flag & MSGFLAG_WHISPER)
825 add_to_tell_history(getuid(ob), 1, 0,
826 "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
827 flag);
828 else
829 add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
830 }
831 return 1;
832}
833
834// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
835// Fehlerbehandlung) ist es jedoch auch mal ein String.
836varargs int Message(string msg, int flag, string indent,
837 string cname, mixed sender)
838{
839 object ti;
840 string verb, reply, *ignore, tin;
841 int em, te;
842 mixed deaf;
843
844 // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
845 // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
846 // damit 'ignoriere name.debug' funktioniert.
847 if( flag == MSGFLAG_CHANNEL ){
848 if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
849 || msg[1..9]=="Warnungen"))
850 {
851 // Missbrauch der Variable 'ignore' als Zwischenspeicher
852 ignore = regexplode( msg, ":| |\\]" );
853 verb = lower_case(ignore[0][1..]);
854 tin = lower_case(ignore[2]);
855 }
856 else
857 {
858 if(cname)
859 verb=lower_case(cname);
860 else
861 verb=query_verb();
862 if( ti = this_interactive() )
863 {
864 tin = getuid(this_interactive());
865 }
866 else
867 {
868 //falls doch kein Objekt...
869 if (objectp(sender))
870 tin=lower_case(sender->name(RAW)||"<Unbekannt>");
871 }
872 }
873 }
874 else {
875 if( ti = this_interactive() )
876 tin = getuid(this_interactive());
877 verb = query_verb();
878 }
879
880 te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
881
882 // fuer "erwidere"
883 if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
884 if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
885 if (flag & MSGFLAG_WHISPER)
886 add_to_tell_history(getuid(ti), 0, 1,
887 capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
888 (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
889 ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
890 " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
891 else
892 add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
893 }
894 }
895 // Hoert der Spieler nicht?
896 em = (ti &&
897 (te || flag & MSGFLAG_SHOUT) &&
898 (QueryProp(P_EARMUFFS) &&
899 (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
900 ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
901
902 // Werden der Sender oder das Verb ignoriert?
903 if(!ti && tin && flag == MSGFLAG_CHANNEL)
904 {
905 if((member(ignore, tin) != -1))
906 {
907 return MESSAGE_IGNORE_YOU;
908 }
909 if(verb && sizeof(filter(ignore, #'check_ignore, verb, tin)) )
910 {
911 return MESSAGE_IGNORE_YOU;
912 }
913 }
914 if (ti && (member(ignore, getuid(ti)) != -1)) {
915 if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
916 efun::tell_object(ti, capitalize(name())+
917 " hoert gar nicht zu, was Du sagst.\n");
918 return MESSAGE_IGNORE_YOU;
919 }
920 if(tin && verb &&
921 sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
922 {
923 if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
924 verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
925 efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
926 return MESSAGE_IGNORE_VERB;
927 }
928 if (flag & MSGFLAG_RTELL) {
929 int at;
930
931 verb = lower_case(old_explode(msg, " ")[0][1..]);
932 at = member(verb, '@');
933 /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
934 add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
935
936 if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
937 return MESSAGE_IGNORE_YOU;
938 else if (at > 0 && member(ignore, verb[at..]) >= 0)
939 return MESSAGE_IGNORE_MUD;
940 }
941
942 // Taubheit/Oropax
943 te |= (flag & MSGFLAG_SAY);
944
945 if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
946 deaf = QueryProp(P_DEAF);
947 if (te)
948 reply = stringp(deaf) ?
949 capitalize(sprintf(deaf, name())) :
950 capitalize(name())+" ist momentan leider taub.\n";
951 }
952 else if (em)
953 reply = capitalize(name())+" hat Oropax in den Ohren.\n";
954
955 msg = break_string(msg, 78, indent,
956 (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
957
958 if(QueryProp(P_BUFFER) &&
959 (deaf ||
960 query_editing(this_object()) ||
961 query_input_pending(this_object())))
962 {
963 deaf = MESSAGE_DEAF;
964 if(flag & MSGFLAG_CACHE)
965 {
966 if(!stringp(reply))
967 reply = name()+" moechte gerade nicht gestoert werden.\n";
968
969 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
970
971 int res = add_to_kobold(msg, 0, 0, 0,
972 objectp(sender) ? sender : ME);
973 if(res == MSG_BUFFERED)
974 {
975
976 reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
977 "genommen.\nEr wird sie spaeter weiterleiten!";
978 deaf = MESSAGE_CACHE;
979 }
980 else {
981 reply += "Die Mitteilung ging verloren, denn "+
982 "der Kobold kann sich nichts mehr merken!";
983 deaf = MESSAGE_CACHE_FULL;
984 }
985 if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
986 efun::tell_object(ti, reply+"\n");
987 }
988 return deaf;
989 }
990 else if((deaf || em) &&
991 ( (flag & MSGFLAG_RTELL) ||
992 (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
993 if (te && ti)
994 efun::tell_object(ti, reply);
995 return MESSAGE_DEAF;
996 }
997
998 _flush_cache(0);
999 if(te && QueryProp(P_AWAY))
1000 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1001
Zesstra1cc6cf32022-02-09 11:49:31 +01001002 if(!objectp(sender) || sender != ME)
Bugfix60f5bc62022-01-21 11:09:56 +01001003 {
Zesstra1cc6cf32022-02-09 11:49:31 +01001004 if (flag & MSGFLAG_SAY)
1005 comm_beep(MA_SAY);
1006 else if (flag & MSGFLAG_TELL)
1007 comm_beep(MA_TELL);
1008 else if (flag & MSGFLAG_CHANNEL)
1009 comm_beep(MA_CHANNEL);
1010 else if (flag & MSGFLAG_SHOUT)
1011 comm_beep(MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001012 }
1013 efun::tell_object(ME, msg);
1014 return MESSAGE_OK;
1015}
1016
1017static int ignoriere(string str)
1018{
1019 str = _unparsed_args(1);
1020 mapping ignore=Query(P_IGNORE, F_VALUE);
1021
1022 if (!str)
1023 {
1024 string* ignarr = m_indices(ignore);
1025 if (!sizeof(ignarr))
1026 tell_object(ME, "Du ignorierst niemanden.\n");
1027 else
1028 ReceiveMsg("Du ignorierst:\n"
1029 + break_string(CountUp(map(sort_array(ignarr, #'> ),
1030 #'capitalize )
1031 ) + ".",78),
1032 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
1033 0,0,this_object());
1034 return 1;
1035 }
1036 // trim spaces from args and convert to lower case.
1037 str = lower_case(trim(str, TRIM_BOTH));
1038
1039 if (member(ignore, str))
1040 {
1041 RemoveIgnore(str);
1042 tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
1043 }
1044 else if (sizeof(ignore)>100)
1045 {
1046 tell_object(ME, "Du ignorierst schon genuegend!\n");
1047 }
1048 else if (AddIgnore(str) == 1)
1049 {
1050 tell_object(ME,
1051 sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
1052 }
1053 else
1054 {
1055 tell_object(ME,
1056 sprintf("'%s' kannst Du nicht ignorieren.\n",str));
1057 }
1058 return 1;
1059}
1060
1061
Zesstra1cc6cf32022-02-09 11:49:31 +01001062private int _alert_filter_flags(int flag, string text)
Bugfix60f5bc62022-01-21 11:09:56 +01001063{
1064 return (flag & QueryProp(P_ALERT));
1065}
1066
Zesstra1cc6cf32022-02-09 11:49:31 +01001067static int _msg_beep(string str)
1068{
1069 int beep_interval;
1070
1071 notify_fail(
1072 "Syntax:\n"
1073 "- klingelton <1 bis 3600 Sekunden>\n"
1074 "- klingelton aus\n"
1075 "- klingelton ein\n"
1076 "- klingelton <+/-><tm / sag / ebenen / ruf / erwaehnung / sonstige / alle / >\n");
1077
1078 if(!sizeof(str))
1079 return 0;
1080 if (regmatch(str,"^[[:alpha:]]", RE_PCRE))
Bugfix60f5bc62022-01-21 11:09:56 +01001081 {
MG Mud User88f12472016-06-24 23:31:02 +02001082 if (str=="aus")
Zesstra1cc6cf32022-02-09 11:49:31 +01001083 SetProp(P_ALERT, QueryProp(P_ALERT) | AL_NO_SOUND);
1084 else if (str=="ein")
1085 SetProp(P_ALERT, QueryProp(P_ALERT) & ~AL_NO_SOUND);
Bugfix60f5bc62022-01-21 11:09:56 +01001086 else
1087 {
1088 mapping flags = ([
Zesstra1cc6cf32022-02-09 11:49:31 +01001089 "tm": MB_TELL, "teilemit": MB_TELL,
1090 "sag": MB_SAY, "sage": MB_SAY,
1091 "ebene": MB_CHANNEL, "ebenen": MB_CHANNEL,
1092 "ruf": MB_SHOUT, "rufe": MB_SHOUT,
Zesstra3a261e52022-02-10 14:00:31 +01001093 "erwaehnung": MB_MENTION, "erwaehnungen": MB_MENTION,
Zesstra1cc6cf32022-02-09 11:49:31 +01001094 "sonstige": MB_MISC, "sonstiges": MB_MISC,
1095 "alle": MB_ALL, "alles": MB_ALL,
1096 ]);
Bugfix60f5bc62022-01-21 11:09:56 +01001097 foreach(string part : explode(str, " ") - ({""}))
1098 {
1099 if(!(part[1..] in flags)) continue;
1100 if(part[0] == '+')
1101 {
1102 SetProp(P_ALERT,
1103 QueryProp(P_ALERT) | flags[part[1..]]);
1104 }
1105 else if(part[0] == '-')
1106 {
1107 SetProp(P_ALERT,
1108 QueryProp(P_ALERT) & ~ flags[part[1..]]);
1109 }
1110 }
1111 }
MG Mud User88f12472016-06-24 23:31:02 +02001112 }
Zesstra1cc6cf32022-02-09 11:49:31 +01001113 else
1114 {
1115 beep_interval = to_int(str);
1116 if(beep_interval >= 0)
1117 {
1118 SetProp(P_MESSAGE_BEEP, beep_interval);
1119 }
1120 }
MG Mud User88f12472016-06-24 23:31:02 +02001121
Zesstra593b2c72019-11-27 23:37:03 +01001122 beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
Bugfix60f5bc62022-01-21 11:09:56 +01001123 mapping text = ([
1124 MB_SAY: "sage",
1125 MB_TELL: "teile mit",
1126 MB_CHANNEL: "Ebenenmeldungen",
Zesstra3a261e52022-02-10 14:00:31 +01001127 MB_MENTION: "Erwaehnungen",
Zesstra1cc6cf32022-02-09 11:49:31 +01001128 MB_SHOUT: "rufe",
1129 MB_MISC: "sonstige",]);
1130 string types = CountUp(map(filter(m_indices(text), #'_alert_filter_flags), text));
Bugfix60f5bc62022-01-21 11:09:56 +01001131 if(!sizeof(types))
1132 {
1133 types = "nichts";
1134 }
1135 ReceiveNotify(
1136 "Klingelton bei "
1137 + types
Zesstra1cc6cf32022-02-09 11:49:31 +01001138 + (beep_interval ? ", alle " + beep_interval + " Sekunden." : ", immer.")
1139 + (QueryProp(P_ALERT) & AL_NO_SOUND ? " Allerdings sind Toene insgesamt "
1140 "bei Dir abgeschaltet. (\'hilfe ton\')" : ""),
Bugfix60f5bc62022-01-21 11:09:56 +01001141 query_verb());
MG Mud User88f12472016-06-24 23:31:02 +02001142 return 1;
1143}
1144
1145static int _msg_prepend(string str) {
MG Mud User88f12472016-06-24 23:31:02 +02001146 notify_fail("Syntax: senderwiederholung ein/aus\n");
1147 if (stringp(str)) {
1148 if (str=="aus")
1149 SetProp(P_MESSAGE_PREPEND,1);
1150 else if (str=="ein")
1151 SetProp(P_MESSAGE_PREPEND,0);
1152 else return 0;
1153 }
1154
Bugfixb1d9b4d2021-09-14 20:07:04 +02001155 ReceiveNotify("Senderwiederholung bei Mitteilungen: "+
Zesstra04f613c2019-11-27 23:32:54 +01001156 (({int})QueryProp(P_MESSAGE_PREPEND) ? "aus" : "ein")+".",
MG Mud User88f12472016-06-24 23:31:02 +02001157 query_verb());
1158
1159 return 1;
1160}
1161
1162static int _communicate(mixed str, int silent)
1163{
1164 string verb;
1165 string myname;
1166 string msg;
1167
1168 if (!str || extern_call()) str=_unparsed_args()||"";
1169 /* str=_unparsed_args()||""; */
1170 verb = query_verb();
1171 if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
1172 if (str==""||str==" "||!str)
1173 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001174 ReceiveNotify("Was willst Du sagen?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001175 return 1;
1176 }
1177 msg=permutate(str);
1178
1179 myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
1180 !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
1181 "":QueryProp(P_PRESAY))+name())||"";
1182
1183 // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
1184 // ist, keine MT_COMM)
Zesstrabd1236a2022-02-09 22:35:59 +01001185 send_room(environment(), msg, MT_LISTEN|MSG_ALERT, MA_SAY,
MG Mud User88f12472016-06-24 23:31:02 +02001186 capitalize(myname)+" sagt: ", ({this_object()}) );
1187
1188 if(!silent)
1189 {
1190 ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1191 MA_SAY, "Du sagst: ", ME);
1192 }
1193 return 1;
1194}
1195
1196static int _shout_to_all(mixed str)
1197{
1198 string pre, myname, realname, wizards_msg, players_msg;
1199 string wizard_prefix, player_prefix;
1200 int chars;
1201
1202 if (!(str=_unparsed_args()))
1203 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001204 ReceiveNotify("Was willst Du rufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001205 return 1;
1206 }
1207 chars=sizeof(str)/2;
1208 if (chars<4) chars=4;
1209 pre = (!IS_LEARNER(ME) ||
1210 QueryProp(P_INVIS) ||
1211 !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
1212 realname = capitalize((pre + capitalize(getuid()))||"");
1213 myname = capitalize(pre + name()||"");
1214 if (QueryProp(P_INVIS))
1215 realname = "("+realname+")";
1216
1217 wizards_msg = permutate(str);
1218 wizard_prefix = myname+" ruft: ";
1219
1220 if(QueryProp(P_FROG)) {
1221 players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
1222 player_prefix = myname+" quakt: ";
1223 }
1224 else {
1225 players_msg = wizards_msg;
1226 player_prefix = wizard_prefix;
1227 }
1228
1229 if(!IS_LEARNER(this_player()))
1230 {
1231 if(QueryProp(P_GHOST)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001232 ReceiveNotify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
MG Mud User88f12472016-06-24 23:31:02 +02001233 MA_SHOUT);
1234 return 1;
1235 }
1236 if (QueryProp(P_SP) <(chars+20))
1237 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001238 ReceiveNotify("Du musst erst wieder magische Kraefte sammeln.",
MG Mud User88f12472016-06-24 23:31:02 +02001239 MA_SHOUT);
Bugfixb1d9b4d2021-09-14 20:07:04 +02001240 ReceiveNotify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
MG Mud User88f12472016-06-24 23:31:02 +02001241 "Ebenen').", MA_SHOUT);
1242 return 1;
1243 }
1244 SetProp(P_SP, QueryProp(P_SP) - chars - 20);
1245 }
1246
1247 ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
Zesstrabd1236a2022-02-09 22:35:59 +01001248 MA_SHOUT, "Du rufst: ", ME);
MG Mud User88f12472016-06-24 23:31:02 +02001249
1250 foreach ( object ob : users()-({this_object()}) )
1251 if ( IS_LEARNER(ob) )
Zesstrabd1236a2022-02-09 22:35:59 +01001252 ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR|MSG_ALERT, MA_SHOUT,
1253 wizard_prefix, this_object());
MG Mud User88f12472016-06-24 23:31:02 +02001254 else
Zesstrabd1236a2022-02-09 22:35:59 +01001255 ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR|MSG_ALERT, MA_SHOUT,
1256 player_prefix, this_object());
MG Mud User88f12472016-06-24 23:31:02 +02001257
1258 return 1;
1259}
1260
1261varargs int _tell(string who, mixed msg)
1262{
1263 object ob;
1264 string away,myname,ret;
Arathornb3051452021-05-13 21:13:03 +02001265 mixed it;
MG Mud User88f12472016-06-24 23:31:02 +02001266 string *xname;
1267 int i,visflag;
1268
1269 if (extern_call() && this_interactive()!=ME) return 1;
1270 if (!who || !msg) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001271 ReceiveNotify("Was willst Du mitteilen?",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001272 return 1;
1273 }
1274
1275 if(who == ERWIDER_PARAM)
1276 {
1277 if (!last_comm_partner)
1278 {
1279 _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
1280 "Du was erwidern\nkoenntest.\n");
1281 return 0;
1282 }
1283 who=last_comm_partner;
1284 }
1285
1286 // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
1287 if (who == ".")
1288 who = ".1";
1289
1290 if ( sscanf(who, ".%d", i) == 1 ) {
1291 if(i > 0 && i <= sizeof(commreceivers))
1292 who = commreceivers[i-1];
1293 else {
1294 _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
1295 return 0;
1296 }
1297 }
1298
1299 xname = explode(who, "@");
1300
Zesstrabd1236a2022-02-09 22:35:59 +01001301 if (sizeof(xname) == 2)
MG Mud User88f12472016-06-24 23:31:02 +02001302 {
1303 if ( QueryProp(P_QP) )
1304 {
Zesstra04f613c2019-11-27 23:32:54 +01001305 if (ret=({string})INETD->_send_udp(xname[1],
MG Mud User88f12472016-06-24 23:31:02 +02001306 ([ REQUEST: "tell",
1307 RECIPIENT: xname[0],
1308 SENDER: getuid(ME),
1309 DATA: msg ]), 1))
1310 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001311 ReceiveNotify(ret, MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001312 }
1313 else
1314 {
1315 write("Nachricht abgeschickt.\n");
1316 add_to_tell_history(who, 1, 0, msg,
Zesstraa31cd5c2016-12-17 20:11:07 +01001317 "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_RTELL, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001318 }
1319 }
1320 else
1321 write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
1322 "Muds etwas mitteilen zu koennen.\n");
1323 return 1;
1324 }
1325
1326 if (!ob=find_player(it = lower_case(who)))
1327 {
1328 it = match_living(it, 0);
1329 if (!stringp(it))
1330 switch(it) {
1331 case -1:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001332 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001333 return 1;
1334 case -2:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001335 ReceiveNotify("Kein solcher Spieler!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001336 return 1;
1337 }
1338 ob = find_player(it) || find_living(it);
1339 if(!ob) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001340 ReceiveNotify("Kein solcher Spieler!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001341 return 1;
1342 }
1343 }
1344
1345 if(QueryProp(P_INVIS)){
1346 if(!IS_LEARNER(ob))
1347 myname = name();
1348 else
1349 myname="("+
1350 ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
1351 capitalize(getuid()) + ")";
1352 }
1353 else
1354 myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
Bugfix45f88ce2017-03-06 14:41:56 +01001355 QueryProp(P_PRESAY):"") + capitalize(getuid(ME));
MG Mud User88f12472016-06-24 23:31:02 +02001356 if (myname && sizeof(myname)) myname=capitalize(myname);
1357 // erstmal an Empfaenger senden
Zesstrabd1236a2022-02-09 22:35:59 +01001358 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_ALERT, MA_TELL,
MG Mud User88f12472016-06-24 23:31:02 +02001359 myname + " teilt Dir mit: ");
1360
1361 // dann evtl. noch an Absender ausgeben...
1362 if (visflag = !ob->QueryProp(P_INVIS) || IS_LEARNER(this_player()))
1363 _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(it) + " mit: ");
1364 // oder irgendwas anderes an den Absender ausgeben...
1365 if (!visflag && interactive(ob))
Bugfixb1d9b4d2021-09-14 20:07:04 +02001366 ReceiveNotify("Kein solcher Spieler!",MA_TELL);
Zesstra04f613c2019-11-27 23:32:54 +01001367 else if (away = ({string})ob->QueryProp(P_AWAY))
MG Mud User88f12472016-06-24 23:31:02 +02001368 ReceiveMsg( break_string( away, 78, capitalize(it)
1369 + " ist gerade nicht da: ", BS_INDENT_ONCE ),
1370 MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
1371 MA_TELL, 0, this_object());
1372 else if (interactive(ob) && (i=query_idle(ob))>=600)
1373 { //ab 10 Mins
1374 if (i<3600)
1375 away=time2string("%m %M",i);
1376 else
1377 away=time2string("%h %H und %m %M",i);
1378
Bugfixb1d9b4d2021-09-14 20:07:04 +02001379 ReceiveNotify(sprintf("%s ist seit %s voellig untaetig.",
MG Mud User88f12472016-06-24 23:31:02 +02001380 capitalize(it),away),
1381 MA_TELL);
1382 }
1383
1384 return 1;
1385}
1386
1387static int _teile(string str)
1388{
1389 string who, message;
1390 if (!(str=_unparsed_args())) return 0;
1391 if (sscanf(str, "%s mit %s", who, message) == 2)
1392 return _tell(who, message,1);
1393 return 0;
1394}
1395static int _teile_mit_alias(string str)
1396{
1397 str = _unparsed_args(), TRIM_LEFT;
1398 if (!str) return 0;
1399 str = trim(str, TRIM_LEFT);
1400 // Ziel muss min. 2 Buchstaben haben (.<nr>)
1401 if (sizeof(str) < 4) return 0;
1402 int pos = strstr(str, " ");
1403 if (pos >= 2)
1404 return _tell(str[..pos-1], str[pos+1..]);
1405 return 0;
1406}
1407
1408static int _erzaehle(string str)
1409{
1410 string who, message;
1411
1412 if (!(str=_unparsed_args())) return 0;
1413 if (sscanf(str, "%s %s", who, message) == 2)
1414 return _tell(who, message,1);
1415 return 0;
1416}
1417
1418static int _whisper(string str)
1419{
1420 object ob;
1421 string who;
1422 string msg;
1423 string myname;
1424
1425 if (!(str=_unparsed_args()) ||
1426 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1427 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001428 ReceiveNotify("Was willst Du wem zufluestern?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001429 return 1;
1430 }
Zesstra6e88b6a2019-11-08 00:25:39 +01001431 who = lower_case(who);
MG Mud User88f12472016-06-24 23:31:02 +02001432 if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001433 ReceiveNotify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001434 return 1;
1435 }
1436
1437 myname = capitalize((((IS_LEARNER(ME) &&
1438 !QueryProp(P_INVIS) &&
1439 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1440 QueryProp(P_PRESAY) : "") + name()) || "");
1441
1442 _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
Bugfixdbdbed52022-01-21 11:14:35 +01001443 MA_SAY, myname + " fluestert Dir zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001444 send_room(environment(),
1445 myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
1446 MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
1447
1448 _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
1449
1450
1451 return 1;
1452}
1453
1454static int _remote_whisper(string str)
1455{
1456 /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
1457 Nachricht nicht in der tell_history verewigt. */
1458
1459 object ob;
1460 string who, it;
1461 string msg;
1462 string myname;
1463
1464 if (!(str=_unparsed_args()) ||
1465 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1466 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001467 ReceiveNotify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001468 return 1;
1469 }
1470
1471 if (!ob=find_player(it = lower_case(who)))
1472 {
1473 it = match_living(it, 0);
1474 if (!stringp(it))
1475 switch(it){
1476 case -1:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001477 ReceiveNotify("Das war nicht eindeutig!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001478 return 1;
1479 case -2:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001480 ReceiveNotify("Kein solcher Spieler!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001481 return 1;
1482 }
1483 ob = find_player(it);
1484 if(!ob) ob = find_living(it);
1485 if(!ob){
Bugfixb1d9b4d2021-09-14 20:07:04 +02001486 ReceiveNotify("Kein solcher Spieler!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001487 return 1;
1488 }
1489 }
1490 if (environment(ob) == environment()) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001491 ReceiveNotify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001492 return 1;
1493 }
1494
1495 myname = capitalize((((IS_LEARNER(ME) &&
1496 !QueryProp(P_INVIS) &&
1497 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1498 QueryProp(P_PRESAY) : "") + name()) || "");
1499
1500 // An Empfaenger senden.
Zesstrabd1236a2022-02-09 22:35:59 +01001501 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE,
1502 MA_EMOTE, myname + " fluestert Dir aus der Ferne zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001503
1504 // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
1505 if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001506 ReceiveNotify("Kein solcher Spieler!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001507 return 1;
1508 }
1509 // sonst eigene Meldung via _recv() ausgeben.
1510 else
1511 _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
1512 "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
1513
1514 return 1;
1515}
1516
1517static int _converse(string arg)
1518{
Bugfixb1d9b4d2021-09-14 20:07:04 +02001519 ReceiveNotify("Mit '**' wird das Gespraech beendet.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001520 if (stringp(arg) && strstr(arg, "-s") == 0)
1521 input_to("_converse_more", INPUT_PROMPT, "]", 1);
1522 else
1523 input_to("_converse_more", INPUT_PROMPT, "]", 0);
1524 return 1;
1525}
1526
1527static int _converse_more(mixed str, int silent)
1528{
1529 if (str == "**") {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001530 ReceiveNotify("Ok.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001531 return 0;
1532 }
1533
1534 if(str != "")
1535 _communicate(str, silent);
1536
1537 input_to("_converse_more", INPUT_PROMPT, "]", silent);
1538 return 1;
1539}
1540
1541private int is_learner(object o) { return IS_LEARNER(o); }
1542
1543static int _shout_to_wizards(mixed str)
1544{
MG Mud User88f12472016-06-24 23:31:02 +02001545 string myname;
MG Mud User88f12472016-06-24 23:31:02 +02001546
1547 str = _unparsed_args();
1548 if (!str||!sizeof(str)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001549 ReceiveNotify("Was willst Du den Magiern zurufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001550 return 1;
1551 }
1552 // Kontrollzeichen rausfiltern.
1553 str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
1554 myname = capitalize(getuid(this_object()));
1555 if (!IS_LEARNER(this_object()))
1556 _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
1557
1558 // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
1559 filter(users(), #'is_learner)->ReceiveMsg(str,
Zesstrabd1236a2022-02-09 22:35:59 +01001560 MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_ALERT,
MG Mud User88f12472016-06-24 23:31:02 +02001561 MA_SHOUT, myname+" an alle Magier: ", this_object());
1562
1563 return 1;
1564}
1565
1566static int _echo(string str) {
1567 if (!IS_SEER(ME) || (!IS_LEARNER(ME)
1568 && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
1569 return 0;
1570
1571 if (!(str=_unparsed_args())) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001572 ReceiveNotify("Was moechtest Du 'echoen'?", 0);
MG Mud User88f12472016-06-24 23:31:02 +02001573 return 1;
1574 }
1575
1576 if (!IS_LEARNER(this_interactive()))
1577 {
1578 if (QueryProp(P_GHOST))
1579 {
1580 _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
1581 return 0;
1582 }
1583 if (QueryProp(P_SP)<ECHO_COST)
1584 {
1585 _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
1586 return 0;
1587 }
1588 SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
1589 str=">\b"+str;
1590 log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
1591 str));
1592 }
1593 // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
1594 // Kommhistory noch im Kobold landen.
1595 send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
1596 MA_UNKNOWN, 0, 0);
1597 return 1;
1598}
1599
1600// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
1601// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
1602// und gefedert.
1603static string *_set_ignore(mixed arg)
1604{
1605 raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
1606 "Benutze AddIgnore/RemoveIgnore()!\n");
1607}
1608// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
1609// nix dran, dass alle TestIgnore() & Co benutzen sollen.
1610static string *_query_ignore() {
1611 mixed ign=Query(P_IGNORE, F_VALUE);
1612 if (mappingp(ign))
1613 return m_indices(ign);
1614 return ({});
1615}
1616
1617public int AddIgnore(string ign) {
1618 // Einige strings sind nicht erlaubt, z.B. konsekutive .
1619 if (!sizeof(ign)
1620 || regmatch(ign,"[.]{2,}",RE_PCRE)
1621 || regmatch(ign," ",RE_PCRE)
1622 || sizeof(explode(ign,"."))>3)
1623 return 0;
1624
1625 mapping ignores=Query(P_IGNORE, F_VALUE);
1626 ignores[ign]=time();
1627 // kein Set() noetig.
1628 return 1;
1629}
1630
1631public int RemoveIgnore(string ign)
1632{
1633 mapping ignores=Query(P_IGNORE,F_VALUE);
1634 m_delete(ignores,ign);
1635 // Kein Set() noetig
1636 return 1;
1637}
1638
1639static int _query_intermud()
1640{
1641 mixed tmp;
1642 return member(pointerp(tmp=Query(P_CHANNELS))?tmp:({}), "Intermud") > -1;
1643}
1644
1645
1646int erwidere(string str)
1647{
1648 str=_unparsed_args();
1649 if (!str) return 0;
1650 return _tell(ERWIDER_PARAM, str ,1);
1651}
1652
1653static int tmhist(string str)
1654{
1655
1656 if (str == "aus") {
1657 tell_history_enabled = TELLHIST_DISABLED;
1658 write("Ok, es wird nichts mehr gespeichert.\n");
1659 if (sizeof(tell_history)) {
1660 tell_history = ([]);
1661 commreceivers = ({});
1662 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1663 }
1664 return 1;
1665 }
1666
1667 if (str == "namen") {
1668 int flag;
1669 tell_history_enabled = TELLHIST_NO_MESSAGE;
1670 write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
1671 foreach (string uid, struct chat_s chat: tell_history)
1672 if (pointerp(chat->msgbuf)) {
1673 chat->msgbuf = 0;
1674 chat->ptr = 0;
1675 flag = 1;
1676 }
1677 if (flag)
1678 write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
1679 return 1;
1680 }
1681
1682 if (str == "ein" || str == "an") {
1683 tell_history_enabled = TELLHIST_ENABLED;
1684 write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
1685 return 1;
1686 }
1687
1688#ifdef TELLHIST_LONGLIFE
1689 if (str == "langlebig") {
1690 tell_history_enabled = TELLHIST_LONGLIFE;
1691 write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
1692 "Ende/Crash/\nReboot gespeichert.\n");
1693 return 1;
1694 }
1695#endif
1696
1697 if (str == "status") {
1698 switch (tell_history_enabled) {
1699 case TELLHIST_DISABLED:
1700 write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
1701 break;
1702 case TELLHIST_NO_MESSAGE:
1703 write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
1704 break;
1705 case TELLHIST_ENABLED:
1706 write("Deine Mitteilungen werden gespeichert.\n");
1707 break;
1708#ifdef TELLHIST_LONGLIFE
1709 case TELLHIST_LONGLIFE:
1710 write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
1711 "Crash/Reboot\ngespeichert.\n");
1712 break;
1713#endif
1714 }
1715 return 1;
1716 }
1717
1718 if (tell_history_enabled == TELLHIST_DISABLED) {
1719 _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
1720 return 0;
1721 }
1722
1723 if (!sizeof(tell_history)) {
1724 _notify_fail("Du hast noch keinem etwas mitgeteilt "
1725 "und noch keine Mitteilungen erhalten.\n");
1726 return 0;
1727 }
1728
1729 if (str && sizeof(str)) {
1730
1731 if (tell_history_enabled < TELLHIST_ENABLED) {
1732 _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
1733 return 0;
1734 }
1735
1736 string uid;
1737 if (member(tell_history, str)) {
1738 // Name gewuenscht, da der String in der History vorkommt.
1739 uid = str;
1740 }
1741 else {
1742 // evtl. ne Zahl angegeben.
1743 int i;
1744 string *partners = sorted_commpartners(0);
1745 if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
1746 uid = partners[i];
1747 else {
1748 notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
1749 return 0;
1750 }
1751 }
1752
1753 mixed *data = tell_history[uid]->msgbuf;
1754 if (!data) {
1755 _notify_fail(
1756 "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
1757 return 0;
1758 }
1759
1760 int ptr = tell_history[uid]->ptr;
1761
1762 More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
1763 data[0..ptr-1],
1764 function string (struct stored_msg_s msg) {
1765 if (!structp(msg)) return "";
Zesstra3a261e52022-02-10 14:00:31 +01001766 return break_string(terminal_colour(msg->msg, colourmap)
1767 + " <"
MG Mud User88f12472016-06-24 23:31:02 +02001768 + strftime("%H:%M:%S",msg->timestamp) + ">", 78,
1769 msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
1770 } ) ) );
1771 return 1;
1772 }
1773
1774 string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
1775 int i;
1776 foreach (string uid : sorted_commpartners(0) ) {
1777 int j;
1778 struct chat_s chat = tell_history[uid];
1779 history += sprintf("%2d.%-4s %s %-11s %d gesendet/%d empfangen\n", ++i,
1780 ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
1781 strftime("%a, %e.%m.%y",chat->time_last_msg),
1782 capitalize(chat->uid), chat->sentcount, chat->recvcount);
1783 }
1784
1785 More(history);
1786
1787 return 1;
1788}
1789
1790static mixed _query_localcmds()
1791{
1792 return ({
1793 ({"kobold", "cmd_kobold",0,0}),
1794 ({"sag","_communicate",0,0}),
1795 ({"sage","_communicate",0,0}),
1796 ({"'","_communicate",1,0}),
1797 ({"mruf","_shout_to_wizards",0,0}),
1798 ({"mrufe","_shout_to_wizards",0,0}),
1799 ({"ruf","_shout_to_all",0,0}),
1800 ({"rufe","_shout_to_all",0,0}),
1801 ({"erzaehl","_erzaehle",0,0}),
1802 ({"erzaehle","_erzaehle",0,0}),
1803 ({"teil","_teile",0,0}),
1804 ({"teile","_teile",0,0}),
1805 ({"tm","_teile_mit_alias",0,0}),
1806 ({"fluester","_whisper",0,0}),
1807 ({"fluestere","_whisper",0,0}),
1808 ({"rfluester","_remote_whisper",0,0}),
1809 ({"rfluestere","_remote_whisper",0,0}),
1810 ({"gespraech","_converse",0,0}),
1811 ({"echo","_echo",0,0}),
1812 ({"ignorier","ignoriere",0,0}),
1813 ({"ignoriere","ignoriere",0,0}),
1814 ({"tmhist","tmhist",0,0}),
1815 ({"erwider","erwidere",0,0}),
1816 ({"erwidere","erwidere",0,0}),
1817 ({"klingelton","_msg_beep",0,0}),
1818 ({"senderwiederholung","_msg_prepend",0,0}),
1819 ({"report","set_report",0,0}),
1820 })+channel::_query_localcmds();
1821}
1822
1823private string *sorted_commpartners(int reversed) {
1824 return sort_array(m_indices(tell_history),
1825 function int (string uid1, string uid2) {
1826 if (reversed)
1827 return tell_history[uid1]->time_last_msg >
1828 tell_history[uid2]->time_last_msg;
1829 else
1830 return tell_history[uid1]->time_last_msg <=
1831 tell_history[uid2]->time_last_msg;
1832 } );
1833}
1834
Zesstra0712bac2020-06-12 09:41:24 +02001835// Setzt den Prompt eines Interactives. Gerufen bei Objekterstellung,
1836// Reconnect und bei Magiern, wenn diese ihren Prompt oder ihr aktuelles
1837// Verzeichnis aendern.
MG Mud User88f12472016-06-24 23:31:02 +02001838static void modify_prompt() {
1839 string text = Query(P_PROMPT, F_VALUE);
1840
1841 if ( !stringp(text) || !sizeof(text) )
1842 text = "> ";
1843 else {
1844 string path = Query(P_CURRENTDIR, F_VALUE);
1845 if (stringp(path) && sizeof(path))
1846 text = regreplace(text,"\\w",path,0); // Pfad einsetzen
1847 }
1848 configure_interactive(this_object(), IC_PROMPT, text);
1849}
1850
1851// Prueft auf Ingoriereeintraege.
1852// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
MG Mud User88f12472016-06-24 23:31:02 +02001853public int TestIgnore(string|string* srcnames)
MG Mud User88f12472016-06-24 23:31:02 +02001854{
1855 mapping ign = Query(P_IGNORE, F_VALUE);
1856 if (stringp(srcnames))
1857 srcnames = ({srcnames});
1858
1859 foreach(string srcname: srcnames)
1860 {
1861 // einfachster Fall, exakter Match
1862 if (member(ign, srcname))
1863 return MSG_IGNORED;
1864 // ansonsten muss aufgetrennt werden.
1865 if (strstr(srcname,".") > -1)
1866 {
1867 string *srcparts=explode(srcname,".");
1868 switch(sizeof(srcparts))
1869 {
1870 // case 0 und 1 kann nicht passieren.
1871 case 3:
1872 // zu pruefen: [sender].aktion.qualifizierer.
1873 // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
1874 // oben schon geprueft. im Spieler geprueft werden muss noch:
1875 // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
1876 if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
1877 || member(ign, "."+srcparts[1]) // .aktion
1878 || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
1879 || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
1880 )
1881 {
1882 return MSG_IGNORED;
1883 }
1884 break;
1885 case 2:
1886 // zu pruefen: spieler.aktion
1887 // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
1888 // oben schon geprueft. Im Spieler zu pruefen ist noch:
1889 // spieler und .aktion
1890 if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
1891 || member(ign, "."+srcparts[1]))
1892 {
1893 return MSG_IGNORED;
1894 }
1895 break;
1896 default: // mehr als 3 Teile...
1897 raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
1898 "is supported. Got: %s\n",srcname));
MG Mud User88f12472016-06-24 23:31:02 +02001899 }
1900 }
1901 }
1902 // Default: nicht ignorieren.
1903 return 0;
1904}
1905
1906#ifdef __LPC_UNIONS__
1907public int TestIgnoreExt(string|string* srcnames)
1908#else
1909public int TestIgnoreExt(mixed srcnames)
1910#endif
1911{
1912 return TestIgnore(srcnames);
1913}
1914
1915// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
1916// Typen.
1917// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
1918private int check_ignores(string msg, int msg_type, string msg_action,
1919 string msg_prefix, object origin)
1920{
1921 // Einige Dinge lassen sich nicht ignorieren.
1922 if (msg_type & (MT_NEWS|MT_NOTIFICATION))
1923 return 0;
1924 // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
1925 // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
1926 // eine ignorierbare msg_action geben.
1927 else if (stringp(msg_action) && origin && origin != ME)
1928 {
1929 string srcname =
1930 (query_once_interactive(origin) ? origin->query_real_name()
1931 : origin->name(WER) || "");
1932 mapping ign = Query(P_IGNORE, F_VALUE);
1933
1934 if (member(ign, srcname))
1935 return MSG_IGNORED;
1936 // vielleicht wird irgendwas a la name.aktion ignoriert?
1937 // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
1938 // Ebenenmeldungen)
1939 if (member(ign, srcname+"."+msg_action))
1940 return MSG_VERB_IGN;
1941 // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
1942 // das reichlich sinnfrei ist.
1943 if (member(ign, "."+msg_action))
1944 return MSG_VERB_IGN;
1945 // Spieler auf Ebenen ignoriert?
1946 // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
1947 if (strstr(msg_action, MA_CHANNEL) == 0)
1948 {
1949 // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
1950 if (member(ign, srcname + "."MA_CHANNEL))
1951 return MSG_IGNORED;
1952 // spieler.ebenen.ebenenname ist oben schon abgedeckt.
1953 // .ebenen halte ich fuer sinnfrei, nicht geprueft.
1954 }
1955 // Spieler aus anderem mud? *seufz*
1956 if (strstr(srcname,"@") > -1)
1957 {
1958 string *srcparts = explode(srcname,"@");
1959 if (sizeof(srcparts)==2)
1960 {
1961 // spieler@?
1962 if (member(ign, srcparts[0]+"@"))
1963 return MSG_IGNORED;
1964 // oder Mud per @mud?
1965 if (member(ign, "@" + srcparts[1]))
1966 return MSG_MUD_IGN;
1967 // BTW: spieler@mud wurde schon ganz oben erfasst.
1968 }
1969 }
1970 }
1971 // Default: nicht ignorieren.
1972 return 0;
1973}
1974
1975// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
1976// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
Bugfixe0fc68f2022-01-07 15:30:54 +01001977// BLindheit/Taubheit). In Zukunft koennten aber weitere Typen und weitere
1978// Kriterien wichtig werden und weitere Argumente uebergeben werden. (Dies
1979// bitte beachten, falls diese Funktion protected/public werden sollte.)
MG Mud User88f12472016-06-24 23:31:02 +02001980// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
1981// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
1982// Sinn gar nix zu tun hat.
1983// Rueckgabe: 0 oder MSG_SENSE_BLOCK
Bugfixe0fc68f2022-01-07 15:30:54 +01001984private int check_senses(int msg_type)
MG Mud User88f12472016-06-24 23:31:02 +02001985{
1986 int senses = msg_type & (MT_LOOK|MT_LISTEN);
1987 // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
1988 // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
1989 // unterdrueckt werden.
1990 if (!senses)
1991 return 0;
Bugfix427a5812022-01-07 15:41:24 +01001992
1993 int orig_senses = senses;
1994
MG Mud User88f12472016-06-24 23:31:02 +02001995 if ((senses & MT_LOOK) && CannotSee(1))
1996 senses &= ~MT_LOOK; // Sinn loeschen
1997
1998 if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
1999 senses &= ~MT_LISTEN;
2000
Bugfix427a5812022-01-07 15:41:24 +01002001 // Wenn kein Sinn mehr ueber ist oder all_types gesetzt ist und nicht mehr
2002 // alle Sinne vorhanden sind, wird die Nachricht nicht wahrgenommen.
2003 if (orig_senses == senses // kein Sinn geloescht, haeufigster Fall
2004 || (!(msg_type&MSG_ALL_SENSES) && senses) ) // min. ein Sinn uebrig
2005 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02002006
Bugfix427a5812022-01-07 15:41:24 +01002007 return MSG_SENSE_BLOCK;
MG Mud User88f12472016-06-24 23:31:02 +02002008}
2009
2010public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
2011 string msg_prefix, object origin)
2012{
2013 if (!msg) return MSG_FAILED;
2014
2015 // Flags und Typen spalten
2016 int flags = msg_type & MSG_ALL_FLAGS;
2017 int type = msg_type & ~flags;
2018
2019 // ggf. defaults ermitteln
2020 origin ||= previous_object();
2021 msg_action ||= comm_guess_action();
2022 type ||= comm_guess_message_type(msg_action, origin);
2023
2024 // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
2025 if (msg_type & MT_DEBUG)
2026 {
2027 if (!QueryProp(P_WIZ_DEBUG)
2028 || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
2029 return MSG_FAILED;
2030 }
2031
2032 // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
2033 // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
2034 // Weiterbearbeitung.
2035 // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
2036 if (!(flags & MSG_DONT_IGNORE))
2037 {
Bugfix427a5812022-01-07 15:41:24 +01002038 // Sinne pruefen.
2039 int res=check_senses(msg_type);
MG Mud User88f12472016-06-24 23:31:02 +02002040 if (res) return res;
2041
2042 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
2043 res=check_ignores(msg, type, msg_action, msg_prefix, origin);
2044 if (res) return res;
2045 }
2046
Zesstra3a261e52022-02-10 14:00:31 +01002047 // Mentions in der Form @Charname werden in Kommunikation oder beim Hoeren
2048 // von Rufen und Sagen markiert und gegebenfalls gibt es ein Pieps dafuer
2049 int mention;
2050 if ((type & MT_COMM)
2051 || ((type & MT_LISTEN) && msg_action in ({MA_SAY, MA_SHOUT}))
2052 )
2053 {
2054 // Um den Charnamen Tags fuer terminal_colour() einfuegen.
2055 // Lookahead und Lookbehind assertions um die Whitespaces um das Wort
2056 // nicht in den Match einzuschliessen (und zu ersetzen).
2057 string tmp = regreplace(msg,
2058 sprintf("(?<=\\s)@%s(?=\\s*)",getuid(ME)),
2059 sprintf("%%^mention%%^@%s%%^normal%%^",
2060 capitalize(getuid(ME))),
2061 RE_PCRE|RE_GLOBAL|RE_CASELESS);
2062 send_debug(ME, tmp);
2063 // Der Vergleich ist weniger schlimm als es aussieht, weil die Strings
2064 // unterschiedlich gross sein werden und daher nicht zeichenweise
2065 // verglichen werden muessen. Es ist dann jedenfalls schneller als
2066 // getrennt mit regmatch oder strstr zu schauen, ob @charname
2067 // vorkommt.
2068 if (tmp != msg)
2069 {
2070 msg = tmp;
2071 mention = 1;
2072 }
2073 }
2074
MG Mud User88f12472016-06-24 23:31:02 +02002075 // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
2076 if ((type & MT_COMM))
2077 {
2078 // erstmal in der Komm-History ablegen, wenn gewuenscht.
2079 if ((!(flags & MSG_DONT_STORE)))
2080 {
2081 string uid;
2082 if (query_once_interactive(origin))
2083 uid = origin->query_real_name();
2084 else
2085 uid = origin->name(WER) || "<unbekannt>";
Zesstraa31cd5c2016-12-17 20:11:07 +01002086 add_to_tell_history(uid, 0, 1, msg, msg_prefix,
2087 (msg_action == MA_TELL ? MSGFLAG_TELL : 0 ) );
MG Mud User88f12472016-06-24 23:31:02 +02002088 }
2089
2090 // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
2091 // Ebenenmeldungen. (Die haben ggf. schon.)
2092 if (stringp(msg_action) && QueryProp(P_AWAY)
2093 && strstr(msg_action, MA_CHANNEL) != 0)
2094 {
2095 // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
2096 if (msg[<1] == '\n')
2097 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
2098 else
2099 msg = msg + " [" + strftime("%H:%M",time()) + "]";
2100 }
2101 // Kobold erlaubt und gewuenscht? Kobold ist fuer die
2102 // direkte Kommunikation mittels MT_COMM vorgesehen.
2103 // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
2104 // ignorieren.
2105 // die if-Konstruktion ist so, weil ich das _flush_cache() im else
2106 // brauche.
2107 if (query_editing(this_object()) || query_input_pending(this_object())
2108 || QueryProp(P_EARMUFFS))
2109 {
2110 if (!(flags & MSG_DONT_BUFFER)
2111 && QueryProp(P_BUFFER))
2112 {
2113 // Nachricht soll im Kobold gespeichert werden.
2114 return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
2115 }
2116 }
2117 else
2118 {
2119 // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
2120 // entleeren.
2121 _flush_cache(0);
2122 }
Bugfix3afcb792022-01-21 22:32:42 +01002123 }
2124
Zesstra3a261e52022-02-10 14:00:31 +01002125 // Farbtags ersetzen
2126 msg = terminal_colour(msg, colourmap);
2127
2128 // Alertton senden?
2129 if ((msg_type & MSG_ALERT) || mention)
2130 comm_beep(msg_action, mention);
MG Mud User88f12472016-06-24 23:31:02 +02002131
2132 // Ausgabenachricht bauen und an den Spieler senden.
2133 if (flags & MSG_DONT_WRAP)
2134 msg = (msg_prefix ? msg_prefix : "") + msg;
2135 else
2136 {
2137 int bsflags = flags & MSG_ALL_BS_FLAGS;
2138 if (QueryProp(P_MESSAGE_PREPEND))
2139 bsflags |= BS_PREPEND_INDENT;
2140 msg = break_string(msg, 78, msg_prefix, bsflags);
2141 }
2142 efun::tell_object(ME, msg);
2143
2144 return MSG_DELIVERED;
2145}