blob: c8981f270a26e61f4cd1998c976df6169573d762 [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>
Zesstra8b5320e2022-02-18 21:10:26 +010021#include <player/comm.h>
MG Mud User88f12472016-06-24 23:31:02 +020022#undef NEED_PROTOTYPES
23
24#include <sys_debug.h>
25
26#include <thing/properties.h>
MG Mud User88f12472016-06-24 23:31:02 +020027#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.
Zesstra7459f252022-02-23 22:47:26 +010068private nosave struct kobold_buffer_s kobold = (<kobold_buffer_s>
MG Mud User88f12472016-06-24 23:31:02 +020069 buf: allocate(32),
70 index: -1,);
Zesstra7459f252022-02-23 22:47:26 +010071// Vault fuer Offline-TMs
72// Der KOBOLD (/secure/kobold) muss immer eine Referenz auf dieses Objekt
73// halten.
74private nosave lwobject "/std/player/comm_vault" commvault;
MG Mud User88f12472016-06-24 23:31:02 +020075
Zesstra3a261e52022-02-10 14:00:31 +010076// Colourmap
77// TODO: spaeter konfigurierbar machen
78private mapping build_colourmap(string ttype="ansi");
79private nosave mapping colourmap = build_colourmap();
80
MG Mud User88f12472016-06-24 23:31:02 +020081varargs string name(int casus, int demonst);
82
83//local property prototypes
84static int _query_intermud();
85public int RemoveIgnore(string ign);
86public int AddIgnore(string ign);
87
88public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
89 string msg_prefix, object origin);
Zesstra7459f252022-02-23 22:47:26 +010090private int check_ignores(string msg, int msg_type, string msg_action,
91 string msg_prefix, object|string origin);
92private varargs void add_struct_tell_history(struct kobold_msg_s msg,
93 int sent, int recv, int flags );
MG Mud User88f12472016-06-24 23:31:02 +020094
95// erzeugt sortierte Liste an Kommunikationspartnern
96private string *sorted_commpartners(int reversed);
97
Zesstra7459f252022-02-23 22:47:26 +010098protected void create()
MG Mud User88f12472016-06-24 23:31:02 +020099{
100 ::create();
Bugfix3afcb792022-01-21 22:32:42 +0100101 Set(P_ALERT, SAVE, F_MODE_AS);
Zesstra47bff292025-07-08 21:40:05 +0200102 SetProp(P_ALERT, MB_TELL|MB_MENTION|MB_MISC);
MG Mud User88f12472016-06-24 23:31:02 +0200103 Set(P_EARMUFFS, 0);
104 Set(P_EARMUFFS, SAVE, F_MODE);
105 Set(P_EARMUFFS, SECURED, F_MODE);
106 Set(P_INTERMUD, SAVE, F_MODE);
107 Set(P_IGNORE, ([]), F_VALUE);
108 Set(P_IGNORE, SAVE, F_MODE);
109 Set(P_BUFFER, SAVE, F_MODE);
Zesstra7459f252022-02-23 22:47:26 +0100110 SetProp(P_BUFFER, KOBOLD_OFFLINE);
MG Mud User88f12472016-06-24 23:31:02 +0200111 Set(P_MESSAGE_PREPEND, SAVE, F_MODE_AS);
112 Set(P_MESSAGE_BEEP, SAVE, F_MODE_AS);
113}
114
115void create_super()
116{
117 set_next_reset(-1);
118}
119
Zesstra3a261e52022-02-10 14:00:31 +0100120private mapping build_colourmap(string ttype)
121{
122 mapping res = ([0:""]);
123 switch(ttype)
124 {
125 case "dumb":
126 return res;
127 case "ansi":
128 res = ([ 0:ANSI_NORMAL, "normal": ANSI_NORMAL,
129 "bold": ANSI_BOLD, "underlined": ANSI_UNDERL,
130 "blink": ANSI_BLINK, "invers": ANSI_INVERS,
131 "black": ANSI_BLACK, "red": ANSI_RED,
132 "green": ANSI_GREEN, "yellow": ANSI_YELLOW,
133 "blue": ANSI_BLUE, "purple": ANSI_PURPLE,
134 "cyan": ANSI_CYAN, "white": ANSI_WHITE,
135 "bg_black": ANSI_BG_BLACK, "bg_red": ANSI_BG_RED,
136 "bg_green": ANSI_BG_GREEN, "bg_yellow": ANSI_BG_YELLOW,
137 "bg_blue": ANSI_BG_BLUE, "bg_purple": ANSI_BG_PURPLE,
138 "bg_cyan": ANSI_BG_CYAN, "bg_white": ANSI_BG_WHITE,
139 "mention": ANSI_BOLD+ANSI_BG_BLUE,
140 ]);
141 break;
142 case "vt100":
143 res += ([0:ANSI_NORMAL, "normal": ANSI_NORMAL,
144 "bold": ANSI_BOLD, "underlined": ANSI_UNDERL,
145 "blink": ANSI_BLINK, "invers": ANSI_INVERS,
146 "mention": ANSI_BOLD,
147 ]);
148 break;
149 default:
150 assert(1, "Ungueltiger Terminaltyp in build_colourmap");
151 }
152 return res;
153}
154
155protected void set_colourmap(string ttype="ansi")
156{
157 colourmap = build_colourmap(ttype);
158}
159
Zesstra7459f252022-02-23 22:47:26 +0100160private void setup_comm_vault()
161{
162 if (!commvault && (QueryProp(P_BUFFER) & KOBOLD_OFFLINE))
MG Mud User88f12472016-06-24 23:31:02 +0200163 {
Zesstra7459f252022-02-23 22:47:26 +0100164 // Schauen, ob ein Vault im KOBOLD existiert.
165 commvault = KOBOLD->RetrieveVault();
166 // Wenn nicht, aber eins gewuenscht ist, wird eins erstellt und in KOBOLD
167 // hinterlegt.
168 if (!commvault)
169 {
170 commvault = new_lwobject("/std/player/comm_vault");
171 KOBOLD->DepositVault(commvault);
172 }
MG Mud User88f12472016-06-24 23:31:02 +0200173 }
174}
175
Zesstra2504b2d2020-05-22 12:30:17 +0200176static int set_report(string str)
177{
MG Mud User88f12472016-06-24 23:31:02 +0200178 int canflags = QueryProp(P_CAN_FLAGS);
MG Mud User88f12472016-06-24 23:31:02 +0200179 if(!str)
180 {
Zesstracf8f2952020-05-22 12:07:52 +0200181 if (stat_reports)
182 {
183 string *res=({});
184 if (stat_reports & DO_REPORT_HP)
185 res+=({"Lebenspunkte"});
186 if (stat_reports & DO_REPORT_SP)
187 res+=({"Konzentrationspunkte"});
188 if (stat_reports & DO_REPORT_POISON)
189 res+=({"Vergiftungen"});
190 if (stat_reports & DO_REPORT_WIMPY)
191 res+=({"Vorsicht"});
MG Mud User88f12472016-06-24 23:31:02 +0200192
Zesstracf8f2952020-05-22 12:07:52 +0200193 tell_object(ME,break_string(
MG Mud User88f12472016-06-24 23:31:02 +0200194 "Dir werden jetzt Veraenderungen Deiner "
195 +CountUp(res) + " berichtet.",78));
Zesstra86ec63b2020-05-22 12:09:56 +0200196 if (GMCP_Status("MG.char") || GMCP_Status("char")
197 || GMCP_Status("Char"))
198 {
199 tell_object(ME,break_string(
200 "Achtung: Dein Client laesst sich den Report per GMCP "
Zesstra2504b2d2020-05-22 12:30:17 +0200201 "(s. 'hilfe GMCP') uebermitteln. Daher wird er Dir nicht "
Zesstra86ec63b2020-05-22 12:09:56 +0200202 "in der Textausgabe des Spiels angezeigt! Moechtest Du "
203 "dies nicht, schalte bitte in Deinem Client GMCP-Module mit "
204 "Namen wie 'MG.char', 'char', 'Char' oder aehnliche aus."));
205 }
MG Mud User88f12472016-06-24 23:31:02 +0200206 }
207 else
208 tell_object(ME,
209 "Alle Statusreports sind ausgeschaltet.\n");
210
211 return 1;
212 }
Zesstra2504b2d2020-05-22 12:30:17 +0200213 else if (str == "aus")
214 {
215 if (stat_reports & DO_REPORT_HP || stat_reports & DO_REPORT_WIMPY)
216 {
MG Mud User88f12472016-06-24 23:31:02 +0200217 string s="";
Zesstra2504b2d2020-05-22 12:30:17 +0200218 if (stat_reports & DO_REPORT_HP)
219 {
MG Mud User88f12472016-06-24 23:31:02 +0200220 str="ebenfalls ";
221 tell_object(ME, "Der Report wurde ausgeschaltet.\n");
222 }
Zesstra2504b2d2020-05-22 12:30:17 +0200223 if ( stat_reports & DO_REPORT_WIMPY )
224 {
MG Mud User88f12472016-06-24 23:31:02 +0200225 tell_object(ME, "Der Vorsicht-Report wurde "+s+
226 "ausgeschaltet.\n");
227 }
228 stat_reports=0;
229 }
Zesstra2504b2d2020-05-22 12:30:17 +0200230 else
231 {
MG Mud User88f12472016-06-24 23:31:02 +0200232 tell_object(ME, "Der Report ist bereits ausgeschaltet.\n");
233 }
234 return 1;
235 }
Zesstra2504b2d2020-05-22 12:30:17 +0200236 else if (str == "ein")
237 {
238 if ( stat_reports & DO_REPORT_HP )
239 {
MG Mud User88f12472016-06-24 23:31:02 +0200240 tell_object(ME, "Der Report ist bereits eingeschaltet.\n");
241 return 1;
242 }
243 tell_object(ME, "Der Report wurde eingeschaltet.\n");
244 stat_reports |= DO_REPORT_HP;
Zesstra2504b2d2020-05-22 12:30:17 +0200245 if (!(canflags & CAN_REPORT_SP))
246 {
247 if (QueryQuest("Hilf den Gnarfen")==1)
248 {
MG Mud User88f12472016-06-24 23:31:02 +0200249 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_SP);
250 stat_reports |= DO_REPORT_SP;
251 }
Zesstra2504b2d2020-05-22 12:30:17 +0200252 else
253 {
MG Mud User88f12472016-06-24 23:31:02 +0200254 tell_object(ME, break_string(
255 "Fuer den Statusreport Deiner Konzentration musst Du jedoch "
256 "zunaechst die Quest \"Hilf den Gnarfen\" bestehen.",78));
257 }
258 }
Zesstra2504b2d2020-05-22 12:30:17 +0200259 else
260 {
MG Mud User88f12472016-06-24 23:31:02 +0200261 stat_reports |= DO_REPORT_SP;
262 }
Zesstra2504b2d2020-05-22 12:30:17 +0200263 if (!(canflags & CAN_REPORT_POISON))
264 {
265 if (QueryQuest("Katzenjammer")==1)
266 {
MG Mud User88f12472016-06-24 23:31:02 +0200267 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_POISON);
268 stat_reports |= DO_REPORT_POISON;
269 }
Zesstra2504b2d2020-05-22 12:30:17 +0200270 else
271 {
MG Mud User88f12472016-06-24 23:31:02 +0200272 tell_object(ME, break_string(
273 "Fuer den Statusreport Deiner Vergiftung musst Du jedoch "
274 "zunaechst die Quest \"Katzenjammer\" bestehen.",78));
275 }
276 }
Zesstra2504b2d2020-05-22 12:30:17 +0200277 else
278 {
MG Mud User88f12472016-06-24 23:31:02 +0200279 stat_reports |= DO_REPORT_POISON;
280 }
281 // Cache loeschen, damit beim naechsten Report-Event alle Daten neu
282 // eingetragen werden muessen. Muss beim Einschalten des Reports
283 // passieren, weil auch in der inaktiven Zeit weiterhin Aenderungen in
284 // status_report() eingehen, so dass der Cache zwar erst einmal leer ist,
285 // aber beim Wiedereinschalten nicht mehr ungueltig waere und somit
286 // veraltete Daten an den Spieler ausgegeben werden. Im unguenstigsten
287 // Fall wuerde das sogar dazu fuehren, dass die veralteten Daten lange
288 // Zeit nicht aktualisiert werden, wenn z.B. P_HP == P_MAX_HP, so dass
289 // kein P_HP-Event mehr eingeht.
290 report_cache=0;
Zesstra2504b2d2020-05-22 12:30:17 +0200291 // Fall-through fuer Statusausgabe
MG Mud User88f12472016-06-24 23:31:02 +0200292 }
Zesstra2504b2d2020-05-22 12:30:17 +0200293 else if (str == "vorsicht")
294 {
295 if (!(canflags & CAN_REPORT_WIMPY))
296 {
297 if (QueryQuest("Schrat kann nicht einschlafen")==1)
298 {
MG Mud User88f12472016-06-24 23:31:02 +0200299 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_WIMPY);
300 tell_object(ME, "Der Vorsicht-Report wurde eingeschaltet.\n");
301 stat_reports |= DO_REPORT_WIMPY;
302 }
Zesstra2504b2d2020-05-22 12:30:17 +0200303 else
304 {
MG Mud User88f12472016-06-24 23:31:02 +0200305 tell_object(ME, break_string(
306 "Fuer den Statusreport Deiner Vorsicht musst Du "
307 "zunaechst die Quest \"Schrat kann nicht einschlafen\" "
308 "bestehen.",78));
309 }
310 }
311 else
312 {
313 stat_reports |= DO_REPORT_WIMPY;
314 }
315 // fuer Seher auch Bericht der Fluchtrichtung einschalten.
316 if ((stat_reports & DO_REPORT_WIMPY)
317 && !(stat_reports & DO_REPORT_WIMPY_DIR)
318 && ((canflags & CAN_REPORT_WIMPY) || IS_SEER(ME)))
319 {
Zesstra2504b2d2020-05-22 12:30:17 +0200320 stat_reports |= DO_REPORT_WIMPY_DIR;
MG Mud User88f12472016-06-24 23:31:02 +0200321 }
Zesstra2504b2d2020-05-22 12:30:17 +0200322 // Fall-through fuer Statusausgabe
MG Mud User88f12472016-06-24 23:31:02 +0200323 }
324 // sendet einmalig genau jetzt den konfigurierten report. Kann zum testen
325 // (von Triggern) oder beim Login benutzt werden, wenn man einen initialen
326 // Datenbestand erhalten will.
327 else if (str=="senden")
328 {
329 // Es wird Ausgabe von LP und Vorsicht getriggert, das sendet beide
330 // Zeilen.
331 status_report(DO_REPORT_HP, QueryProp(P_HP));
332 status_report(DO_REPORT_WIMPY, QueryProp(P_WIMPY));
333 return 1;
334 }
335 else
336 return 0;
337 // nur aktuellen Zustand berichten
338 set_report(0);
339 return 1;
340}
341
342private string get_poison_desc(int p) {
343 string ret;
344 if ( intp(p) ) {
345 switch(p) {
346 case 0: ret="keins"; break;
347 case 1..3: ret="leicht"; break;
348 case 4..8: ret="gefaehrlich"; break;
349 default: ret="sehr ernst"; break;
350 }
351 return ret;
352 }
353 else return "(nicht verfuegbar)";
354}
355
356// sprintf()-Formatstrings fuer die Reportausgabe.
357#define REPORTLINE "LP: %3d, KP: %3s, Gift: %s.\n"
358#define REPORTLINE_WIMPY "Vorsicht: %d, Fluchtrichtung: %s.\n"
359// Defines zur Adressierung der Cache-Eintraege
360#define REP_HP 0
361#define REP_SP 1
362#define REP_POISON 2
363
364protected void status_report(int type, mixed val) {
365 // Wenn der Spieler GMCP hat und das sich um die Information kuemmert,
366 // erfolgt keine textuelle Ausgabe mehr. Daher return, wenn GMCP_Char()
367 // erfolg vermeldet hat.
368 int flags = QueryProp(P_CAN_FLAGS);
369 switch (type) {
370 case DO_REPORT_HP:
371 if (GMCP_Char( ([ P_HP: val ]) ) ) return;
372 break;
373 case DO_REPORT_SP:
374 if (!(flags & CAN_REPORT_SP)) return;
375 if (GMCP_Char( ([ P_SP: val ]) ) ) return;
376 break;
377 case DO_REPORT_POISON:
378 if (!(flags & CAN_REPORT_POISON)) return;
379 if (GMCP_Char( ([ P_POISON: val ]) ) ) return;
380 break;
381 case DO_REPORT_WIMPY:
382 if (!(flags & CAN_REPORT_WIMPY)) return;
383 if (GMCP_Char( ([ P_WIMPY: val ]) ) ) return;
384 break;
385 case DO_REPORT_WIMPY_DIR:
386 if (!(flags & CAN_REPORT_WIMPY_DIR)) return;
387 if (GMCP_Char( ([ P_WIMPY_DIRECTION: val ]) ) ) return;
388 break;
389 }
390
391 // konventionelle textuelle Ausgabe des Reports ab hier.
392 if (!(type & stat_reports))
393 return;
394
395 if ( !report_cache ) {
396 report_cache = ({
397 QueryProp(P_HP),
398 (stat_reports&DO_REPORT_SP) ? to_string(QueryProp(P_SP)) : "###",
399 (stat_reports&DO_REPORT_POISON) ?
400 get_poison_desc(QueryProp(P_POISON)) : "(nicht verfuegbar)"
401 });
402 }
403
404 switch(type) {
405 // LP berichten: Cache aktualisieren und Meldung ausgeben.
406 case DO_REPORT_HP:
407 report_cache[REP_HP]=val;
408 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
409 report_cache[REP_SP], report_cache[REP_POISON]));
410 break;
411 // KP berichten: Wenn der Spieler den Report freigeschaltet hat,
412 // wird bei Aenderungen gemeldet. Wenn nicht, aendert sich nur der
413 // Cache-Eintrag. So wird verhindert, dass ein Spieler ueber KP-
414 // Veraenderungen auch dann informiert wuerde, wenn er den KP-Report
415 // gar nicht benutzen koennte.
416 case DO_REPORT_SP:
417 report_cache[REP_SP]=to_string(val);
418 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
419 report_cache[REP_SP], report_cache[REP_POISON]));
420 break;
421 // Giftstatus berichten: Wenn der Giftreport freigeschaltet ist,
422 // Cache aktualisieren und berichten. Wenn nicht, aendert sich nur
423 // der Cache-Eintrag. Erlaeuterung hierzu s.o. beim KP-Report.
424 case DO_REPORT_POISON:
425 report_cache[REP_POISON] = get_poison_desc(val);
426 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
427 report_cache[REP_SP], report_cache[REP_POISON]));
428 break;
429 // Vorsicht-Report: kann ohne weitere Abfragen ausgegeben werden, da
430 // alle noetigen Checks schon zu Beginn dieser Funktion erledigt wurden.
431 // Lediglich der Inhalt der Meldung muss abhaengig vom Seherstatus
432 // konfiguriert werden.
433 case DO_REPORT_WIMPY:
434 string res;
435 if (IS_SEER(ME)) {
436 // QueryProp() aus Kostengruenden im if(), damit die Aufruf-
437 // Haeufigkeit zumindest ein wenig reduziert wird.
438 string dir = QueryProp(P_WIMPY_DIRECTION)||"keine";
439 res = sprintf(REPORTLINE_WIMPY, val, dir);
440 }
441 else
442 res = sprintf(REPORTLINE_WIMPY, val, "(nicht verfuegbar)");
443 tell_object(ME, res);
444 break;
445 // Fluchtrichtungs-Report: wird nur bei Sehern ausgegeben, damit
446 // nicht auch Spieler eine VS-/FR-Meldung bekommen, wenn z.B. eine
447 // externe Manipulation der Fluchtrichtung stattfindet, sie aber den
448 // Report mangels Seherstatus gar nicht freigeschaltet haben.
449 case DO_REPORT_WIMPY_DIR:
450 if (IS_SEER(ME)) {
451 if (!val) val = "keine";
452 tell_object(ME,sprintf(REPORTLINE_WIMPY, QueryProp(P_WIMPY), val));
453 }
454 break;
455 }
456}
457
458#undef REPORTLINE
459#undef REPORTLINE_WIMPY
460#undef REP_HP
461#undef REP_SP
462#undef REP_POISON
463
464private string permutate(string msg)
465{
466 // Kontrollzeichen rausfiltern. *seufz*
467 msg = regreplace(msg,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
468 object ob=QueryProp(P_PERM_STRING);
469 if (!objectp(ob))
470 return msg;
471
Zesstra04f613c2019-11-27 23:32:54 +0100472 return ({string})ob->permutate_string(msg)||"";
MG Mud User88f12472016-06-24 23:31:02 +0200473}
474
475// neue nachricht an den Kobold anhaengen
476// Rueckgabewerte: MSG_BUFFER_FULL oder MSG_BUFFERED
477private int add_to_kobold(string msg, int msg_type, string msg_action,
478 string msg_prefix, object origin)
479{
480 // Nachricht soll im Kobold gespeichert werden.
481 // Kobold speichert Rohdaten und gibt spaeter das ganze auch wieder via
482 // ReceiveMsg() aus - dabei wird MSG_DONT_BUFFER | MSG_DONT_STORE gesetz,
483 // damit keine erneute Speicher in Kobold oder Komm-History erfolgt.
484
485 // wenn der Puffer zu klein ist, Groesse verdoppeln, wenn noch unterhalb
486 // des Limits.
487 if (kobold->index >= sizeof(kobold->buf)-1) {
488 if (sizeof(kobold->buf) < MAX_KOBOLD_LIMIT)
489 kobold->buf += allocate(sizeof(kobold->buf));
490 else
491 return MSG_BUFFER_FULL;
492 }
493 kobold->index = kobold->index +1;
494 // neue Nachricht an den Puffer anhaengen.
495 string sendername = query_once_interactive(origin) ?
496 origin->query_real_name() :
497 origin->name(WER) || "<Unbekannt>";
Zesstraa5fda4a2022-01-06 17:31:44 +0100498 kobold->buf[kobold->index] = (<kobold_msg_s> msg: msg,
MG Mud User88f12472016-06-24 23:31:02 +0200499 type : msg_type, action : msg_action, prefix : msg_prefix,
500 sendername : sendername);
501 return MSG_BUFFERED;
502}
503
Zesstra7459f252022-02-23 22:47:26 +0100504// speichert den Inhalt vom commvault im Kobold und der TM-History
505private void process_comm_vault(lwobject "/std/player/comm_vault" vault)
506{
507 struct kobold_msg_s *buffer = vault.Retrieve();
508 if (!sizeof(buffer))
509 return;
510 foreach(struct kobold_msg_s msg: buffer)
511 {
512 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
513 int res = check_ignores(msg.msg, msg.type, msg.action, msg.prefix,
514 msg.sendername);
515 if (res) {
516 // Nachricht wegwerfen. Aber ggf. den Absender informieren, wenn der
517 // online ist und wir nicht Invis
518 object pl = find_player(msg.sendername);
519 if (pl &&
520 (!QueryProp(P_INVIS) || IS_LEARNER(pl)) )
521 pl->ReceiveNotify(sprintf("Deine Nachricht an %s wurde "
522 "ignoriert.",capitalize(getuid(this_object()))), MA_TELL);
523 continue;
524 }
525
526 // wenn der Puffer zu klein ist, Groesse verdoppeln.
527 // Keine Pruefung hier, weil das vault schon die Groesse beschraenkt und
528 // der Inhalt auf jeden Fall passen soll.
529 if (kobold->index >= sizeof(kobold->buf)-1)
530 kobold->buf += allocate(sizeof(kobold->buf));
531 kobold->index += 1;
532 kobold->buf[kobold->index] = msg;
533
534 // TM-History
535 add_struct_tell_history(msg, 0, 1, MSGFLAG_TELL);
536 }
537 vault.Empty();
538}
539
MG Mud User88f12472016-06-24 23:31:02 +0200540private void _flush_cache(int verbose) {
541 // nur mit genug Evalticks ausgeben.
Zesstra7459f252022-02-23 22:47:26 +0100542 if (get_eval_cost() < 500000) return;
MG Mud User88f12472016-06-24 23:31:02 +0200543 if (kobold->index >= 0)
544 {
545 ReceiveMsg("Ein kleiner Kobold teilt Dir folgendes mit:",
546 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
547 0, 0, this_object());
548 int prepend = QueryProp(P_MESSAGE_PREPEND);
549 foreach(int i: 0 .. kobold->index) // '0 ..' ist wichtig!
550 {
Zesstraa5fda4a2022-01-06 17:31:44 +0100551 struct kobold_msg_s msg = kobold->buf[i];
MG Mud User88f12472016-06-24 23:31:02 +0200552 // dies ist dient der Fehlerabsicherung, falls es nen Fehler (z.B. TLE)
553 // in der Schleife unten gab: dann ist index nicht auf -1 gesetzt
554 // worden, aber einige Nachrichten sind schon geloescht.
555 if (!structp(msg)) continue;
Zesstra7459f252022-02-23 22:47:26 +0100556 // Im folgenden nicht den string in der struct aendern (die wird ggf.
557 // auch noch von der TM-History gebraucht).
558 string msgstr = msg.msg;
559 // Wenn Nachricht schon laenger her ist, Uhrzeit anhaengen, aber ggf.
560 // muss ein \n abgeschnitten werden.
561 if (msg.timestamp < time() - 3600)
562 {
563 if (msgstr[<1] == '\n')
564 msgstr = msgstr[0..<2]
565 + " [" + strftime("%d.%m.%y %T", msg.timestamp) + "]";
566 else
567 msgstr = msgstr
568 + " [" + strftime("%d.%m.%y %T", msg.timestamp) + "]";
569 }
MG Mud User88f12472016-06-24 23:31:02 +0200570 // Ausgabe via efun::tell_object(), weil die Arbeit von ReceiveMsg()
Zesstra3a261e52022-02-10 14:00:31 +0100571 // schon getan wurde. Allerdings muessen wir uns noch um den Umbruch
572 // und Farben kuemmern.
Zesstra7459f252022-02-23 22:47:26 +0100573 msgstr = terminal_colour(msgstr, colourmap);
MG Mud User88f12472016-06-24 23:31:02 +0200574 if ((msg->type) & MSG_DONT_WRAP)
Zesstra7459f252022-02-23 22:47:26 +0100575 msgstr = (msg->prefix ? msg->prefix : "") + msgstr;
MG Mud User88f12472016-06-24 23:31:02 +0200576 else
577 {
578 int bsflags = msg->type & MSG_ALL_BS_FLAGS;
579 if (prepend)
580 bsflags |= BS_PREPEND_INDENT;
Zesstra7459f252022-02-23 22:47:26 +0100581 msgstr = break_string(msgstr, 78, msg->prefix, bsflags);
MG Mud User88f12472016-06-24 23:31:02 +0200582 }
Zesstra7459f252022-02-23 22:47:26 +0100583 efun::tell_object(this_object(), msgstr);
MG Mud User88f12472016-06-24 23:31:02 +0200584 kobold->buf[i]=0;
585 }
586 kobold->index=-1;
587 }
588 else if (verbose)
589 {
590 ReceiveMsg("Der kleine Kobold hat leider nichts Neues fuer Dich.",
591 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
592 0, 0, this_object());
593 }
594}
595
596varargs int cmd_kobold(string arg)
597{
Zesstra7459f252022-02-23 22:47:26 +0100598 if (!sizeof(arg))
599 {
600 _flush_cache(1);
601 return 1;
602 }
MG Mud User88f12472016-06-24 23:31:02 +0200603 switch(arg)
604 {
605 case "ein":
Zesstra7459f252022-02-23 22:47:26 +0100606 SetProp(P_BUFFER, KOBOLD_ONLINE|KOBOLD_OFFLINE);
607 ReceiveNotify("Der Kobold merkt sich jetzt alles!");
608 break;
609 case "online":
610 SetProp(P_BUFFER, KOBOLD_ONLINE);
611 ReceiveNotify("Der Kobold merkt sich jetzt alles, "
612 "wenn Du online bist!");
613 break;
614 case "offline":
615 SetProp(P_BUFFER, KOBOLD_OFFLINE);
616 ReceiveNotify("Der Kobold merkt sich jetzt alles, "
617 "wenn Du offline bist!");
618 break;
MG Mud User88f12472016-06-24 23:31:02 +0200619 case "aus":
620 SetProp(P_BUFFER, 0);
Zesstra7459f252022-02-23 22:47:26 +0100621 ReceiveNotify("Der Kobold wird Dich nicht stoeren!");
622 break;
623 default:
624 ReceiveNotify("Der Kobold sagt: Was soll ich mir denn merken? "
625 "('ein', 'aus', 'offline' oder 'online')");
626 return 1;
MG Mud User88f12472016-06-24 23:31:02 +0200627 }
Zesstra7459f252022-02-23 22:47:26 +0100628 if (QueryProp(P_BUFFER) & KOBOLD_OFFLINE)
629 setup_comm_vault();
630 else
631 {
632 // Comm-Vault entfernen. Aber zur Sicherheit nochmal abrufen und
633 // verarbeiten (sollte aber eigentlich ueberfluessig sein)
634 commvault = KOBOLD->ForgetVault();
635 if (commvault) {
636 process_comm_vault(commvault);
637 commvault = 0;
638 }
639 }
MG Mud User88f12472016-06-24 23:31:02 +0200640 return 1;
641}
642
643public int TestIgnoreSimple(string *arg)
Zesstrade642d22019-11-23 17:22:34 +0100644{ mapping ignore;
MG Mud User88f12472016-06-24 23:31:02 +0200645
646 if (!pointerp(arg) || !mappingp(ignore=Query(P_IGNORE,F_VALUE)))
647 return 0;
648 foreach(string s: arg)
649 {
650 if (member(ignore,s))
651 return 1;
652 }
653 return 0;
654}
655
656//TODO: deprecated - entfernen, wenn Message() entfernt wird.
657private int check_ignore(mixed ignore, string verb, string name)
658{
659 if (ignore == verb)
660 return 1;
661 ignore = explode(ignore, ".");
662 return ((sizeof(ignore) > 1) &&
663 (name == ignore[0] && member(ignore[1..], verb) != -1));
664}
665
Zesstra3a261e52022-02-10 14:00:31 +0100666// Die Nachricht kommt mit einem Alert oder Mention. comm_beep() prueft, ob
667// ein Piepston ausgegeben werden soll (langfristig andere Benachrichtigungen
668// geplant!).
669// Ueblicherweise werden nur alle <P_MESSAGE_BEEP> Sekunden Toene
670// uebermittelt.
671private void comm_beep(string msg_action, int mention=0)
Bugfix60f5bc62022-01-21 11:09:56 +0100672{
Zesstra1cc6cf32022-02-09 11:49:31 +0100673 // Wenn Alerts komplett abgeschaltet sind, gibts nix.
674 int alert_config = ({int})QueryProp(P_ALERT);
675 if (alert_config & AL_NO_SOUND)
676 return; // kein ton
677 // und maximal alle P_MESSAGE_BEEP Sekunden aufmerksam machen; bei 0s ist
678 // jedes Mal erlaubt.
Zesstra04f613c2019-11-27 23:32:54 +0100679 int beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
Zesstra1cc6cf32022-02-09 11:49:31 +0100680 if ((time()-last_beep_time) < beep_interval)
681 return;
682 int required;
683 switch(msg_action)
Bugfix60f5bc62022-01-21 11:09:56 +0100684 {
Zesstra1cc6cf32022-02-09 11:49:31 +0100685 case MA_TELL:
686 required |= MB_TELL;
687 break;
688 case MA_SAY:
689 required |= MB_SAY;
690 break;
691 case MA_CHANNEL:
692 required |= MB_CHANNEL;
693 break;
694 case MA_SHOUT:
695 required |= MB_SHOUT;
696 break;
697 default:
698 // Alle anderen Aktionen, die keine gesonderten Aktionsfilter haben
699 required |= MB_MISC;
700 break;
Bugfix60f5bc62022-01-21 11:09:56 +0100701 }
Zesstra3a261e52022-02-10 14:00:31 +0100702 if (mention)
703 required |= MB_MENTION;
704
Zesstra1cc6cf32022-02-09 11:49:31 +0100705 // wenn in alert min. ein notwendiges Flag drin ist darf gepiepst werden
706 if (required & alert_config)
707 {
708 last_beep_time = time();
709 binary_message(b"\a", 1);
710 }
MG Mud User88f12472016-06-24 23:31:02 +0200711}
712
Zesstra7459f252022-02-23 22:47:26 +0100713private varargs void add_struct_tell_history(struct kobold_msg_s msg,
714 int sent, int recv, int flags )
MG Mud User88f12472016-06-24 23:31:02 +0200715{
716 /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
717 Als Wert ist eine Strukur vom Typ chat_s eingetragen.
Zesstra7459f252022-02-23 22:47:26 +0100718 Strukturen chat_s und kobold_msg_s sind in /std/player/comm_structs.c
MG Mud User88f12472016-06-24 23:31:02 +0200719 definiert.
720 TODO fuer spaeter, gerade keine Zeit fuer:
721 Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
722 jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
723 weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
724 Element ist aeltestes Gespraech).
725 */
726
Zesstra996bafe2022-02-14 22:42:39 +0100727 // Gespraechspartner fuer erwidere auch ohne tmhist speichern.
Zesstraa31cd5c2016-12-17 20:11:07 +0100728 if (flags & (MSGFLAG_TELL|MSGFLAG_RTELL))
Zesstra7459f252022-02-23 22:47:26 +0100729 last_comm_partner = msg.sendername;
MG Mud User88f12472016-06-24 23:31:02 +0200730
731 // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
732 // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
733 // Anfang.
734 if (sent) {
735 if (!sizeof(commreceivers))
Zesstra7459f252022-02-23 22:47:26 +0100736 commreceivers = ({msg.sendername});
737 else if (commreceivers[0] != msg.sendername) {
MG Mud User88f12472016-06-24 23:31:02 +0200738 // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
739 // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
740 // entfernen.
741 // TODO: Effizienter gestalten.
Zesstra7459f252022-02-23 22:47:26 +0100742 commreceivers = ({msg.sendername}) + (commreceivers-({msg.sendername}));
MG Mud User88f12472016-06-24 23:31:02 +0200743 // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
744 // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
745 // commreceivers drin sind.)
746 if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
747 commreceivers = commreceivers[0..MAX_SAVED_CHATS];
748 }
749 }
750
Zesstra996bafe2022-02-14 22:42:39 +0100751 // mit eingeschalteter History weitere Details speichern.
MG Mud User88f12472016-06-24 23:31:02 +0200752 if (!tell_history_enabled)
753 return;
754
Zesstra7459f252022-02-23 22:47:26 +0100755 if (msg.msg[<1] == '\n')
756 msg.msg = msg.msg[..<2];
MG Mud User88f12472016-06-24 23:31:02 +0200757
758 struct chat_s chat;
759 // Gespraechspartner unbekannt?
Zesstra7459f252022-02-23 22:47:26 +0100760 if (!member(tell_history, msg.sendername)) {
MG Mud User88f12472016-06-24 23:31:02 +0200761 // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
762 // dazu kommt.
763 if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
764 string deluid;
765 int zeit = __INT_MAX__;
766 foreach(string tuid, chat : tell_history) {
767 // aeltestes Gespraech suchen
768 if (zeit > chat->time_last_msg) {
769 deluid = tuid;
770 zeit = chat->time_last_msg;
771 }
772 }
773 // aeltestes Gespraech raus.
774 m_delete(tell_history, deluid);
775 if (member(commreceivers,deluid)>-1)
776 commreceivers-=({deluid});
777 }
778 // neues Gespraech anlegen
Zesstra7459f252022-02-23 22:47:26 +0100779 chat = (<chat_s> uid: msg.sendername, time_first_msg: msg.timestamp,
780 time_last_msg: msg.timestamp,
MG Mud User88f12472016-06-24 23:31:02 +0200781 sentcount: sent, recvcount: recv,
782 msgbuf: 0, ptr: 0 );
Zesstra7459f252022-02-23 22:47:26 +0100783 tell_history[msg.sendername] = chat;
MG Mud User88f12472016-06-24 23:31:02 +0200784 }
785 else {
786 // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
Zesstra7459f252022-02-23 22:47:26 +0100787 chat = tell_history[msg.sendername];
788 chat->time_last_msg = msg.timestamp;
MG Mud User88f12472016-06-24 23:31:02 +0200789 chat->sentcount += sent;
790 chat->recvcount += recv;
791 }
792
Zesstra996bafe2022-02-14 22:42:39 +0100793 // ggf. auch INhalte von Gespraechen speichern
MG Mud User88f12472016-06-24 23:31:02 +0200794 if (tell_history_enabled < TELLHIST_ENABLED)
795 return;
796
797 // ggf. Array fuer Messages anlegen
798 if (!pointerp(chat->msgbuf))
799 chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
800
Zesstra7459f252022-02-23 22:47:26 +0100801 // neue Struct ins Array schreiben
802 chat->msgbuf[chat->ptr] = msg;
MG Mud User88f12472016-06-24 23:31:02 +0200803 // Index auf naechste Messagestruktur ermitteln
804 chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
Zesstra7459f252022-02-23 22:47:26 +0100805}
806
807private varargs void add_to_tell_history( string uid, int sent, int recv,
808 string message, string indent, int flags )
809{
810 //TODO: Entfernen, wenn das nicht mehr passiert.
811 if (!stringp(uid))
812 {
813 ReceiveMsg(sprintf(
814 "\nadd_to_tell_history(): got bad uid argument %O."
815 "sent: %d, recv: %d, flags: %d, msg: %s",
816 uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);
817 }
818 // Message-Struktur anlegen
819 struct kobold_msg_s msg = (<kobold_msg_s> msg: message, prefix: indent,
820 sendername: uid, timestamp: time());
821 add_struct_tell_history(msg, sent, recv, flags);
MG Mud User88f12472016-06-24 23:31:02 +0200822}
823
Zesstra996bafe2022-02-14 22:42:39 +0100824protected void clear_tell_history(int force)
MG Mud User88f12472016-06-24 23:31:02 +0200825{
826 /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
827 sofern der Spieler nichts abweichendes eingestellt hat. */
828
Zesstra996bafe2022-02-14 22:42:39 +0100829 // bei manuellem Loeschen (force==1) immer loeschen, ansonsten nur, wenn die
830 // History nicht "long-life" ist.
831 if (tell_history_enabled < TELLHIST_LONGLIFE || force)
832 {
833 tell_history = ([]);
834 commreceivers = ({});
835 }
MG Mud User88f12472016-06-24 23:31:02 +0200836}
837
838protected void reset(void)
839{
840 /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
841 Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
842 wie bei einem "schlafe ein" die Mitteilungen geloescht. */
843
844 if (!interactive())
Zesstra996bafe2022-02-14 22:42:39 +0100845 clear_tell_history(0);
MG Mud User88f12472016-06-24 23:31:02 +0200846
847}
848
849// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
850// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
851// werden soll.
852private void _send(object ob, string msg, int msg_type,
853 string msg_action, string msg_prefix)
854{
855 int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
Zesstra8b5320e2022-02-18 21:10:26 +0100856
MG Mud User88f12472016-06-24 23:31:02 +0200857 switch(res) {
858 case MSG_DELIVERED:
859 break; // nix machen
860 case MSG_BUFFERED:
Zesstra62b4a862022-12-23 20:08:16 +0100861 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200862 "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
863 "genommen. Er wird sie spaeter weiterleiten!",
864 MT_NOTIFICATION, msg_action, 0, this_object());
865 break;
866 case MSG_IGNORED:
867 case MSG_VERB_IGN:
868 case MSG_MUD_IGN:
869 ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
870 MT_NOTIFICATION, msg_action, 0, this_object());
871 break;
872 case MSG_SENSE_BLOCK:
873 ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
874 MT_NOTIFICATION, msg_action, 0, this_object());
875 break;
876 case MSG_BUFFER_FULL:
Zesstra62b4a862022-12-23 20:08:16 +0100877 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200878 "Die Mitteilung ging verloren, denn der Kobold kann sich "
879 "nichts mehr merken!", MT_NOTIFICATION, msg_action,
880 0, this_object());
881 break;
882 default:
883 ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
884 "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
885 break;
886 }
887}
888
Zesstra8b5320e2022-02-18 21:10:26 +0100889// aehnlich wie _send(), aber gerufen, wenn die Empfaengerin nicht online ist,
890// aber ein Kobold-Vault deponiert hat und versucht werden soll, dort eine
891// Nachricht zu hinterlegen.
892private void _send_to_kobold(string pluid, string msg, int msg_type,
893 string msg_action, string msg_prefix)
894{
895 int res = KOBOLD->DepositMsg(pluid, msg, msg_type, msg_action,
896 msg_prefix, ME);
897 switch(res) {
898 case MSG_BUFFERED:
899 ReceiveMsg(sprintf("%s ist gerade nicht online, aber Deine Botschaft "
900 "wurde von einem fuer kleinen Kobold aufgeschrieben und er wird "
901 "versuchen, sie %s bei naechster Gelegenheit zu uebermitteln. "
902 "Der Kobold macht Dich darauf aufmerksam, dass er aber keine "
903 "Uebermittlung garantieren kann.",
904 capitalize(pluid), capitalize(pluid)),
905 MT_NOTIFICATION, msg_action, 0, this_object());
906 break;
907 case MSG_BUFFER_FULL:
908 ReceiveMsg(sprintf("%s ist gerade nicht online und leider ist auf "
909 "der Schriftrolle des kleinen Kobolds kein Platz mehr fuer "
910 "Deine Botschaft. Vielleicht schickst Du %s besser einen Brief.",
911 capitalize(pluid), capitalize(pluid)),
912 MT_NOTIFICATION, msg_action, 0, this_object());
913 break;
914 default:
915 ReceiveMsg(sprintf("%s ist gerade nicht online und leider konnte "
916 "sich der kleine Kobold Deine Botschaft nicht merken. "
917 "Vielleicht schickst Du %s besser einen Brief.",
918 capitalize(pluid),capitalize(pluid)),
919 MT_NOTIFICATION, msg_action, 0, this_object());
920 break;
921 }
922}
923
MG Mud User88f12472016-06-24 23:31:02 +0200924// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
925// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
926// die Nachricht ggf. in den Kommhistory erfasst wird.
927// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
928protected varargs int _recv(object ob, string message, int flag, string indent)
929{
930 write(break_string(message, 78, indent,
931 QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
932 if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
933 query_once_interactive(ob))
934 {
935 if (flag & MSGFLAG_WHISPER)
936 add_to_tell_history(getuid(ob), 1, 0,
937 "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
938 flag);
939 else
940 add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
941 }
942 return 1;
943}
944
945// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
946// Fehlerbehandlung) ist es jedoch auch mal ein String.
947varargs int Message(string msg, int flag, string indent,
948 string cname, mixed sender)
949{
950 object ti;
951 string verb, reply, *ignore, tin;
952 int em, te;
953 mixed deaf;
954
955 // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
956 // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
957 // damit 'ignoriere name.debug' funktioniert.
958 if( flag == MSGFLAG_CHANNEL ){
959 if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
960 || msg[1..9]=="Warnungen"))
961 {
962 // Missbrauch der Variable 'ignore' als Zwischenspeicher
963 ignore = regexplode( msg, ":| |\\]" );
964 verb = lower_case(ignore[0][1..]);
965 tin = lower_case(ignore[2]);
966 }
967 else
968 {
969 if(cname)
970 verb=lower_case(cname);
971 else
972 verb=query_verb();
973 if( ti = this_interactive() )
974 {
975 tin = getuid(this_interactive());
976 }
977 else
978 {
979 //falls doch kein Objekt...
980 if (objectp(sender))
981 tin=lower_case(sender->name(RAW)||"<Unbekannt>");
982 }
983 }
984 }
985 else {
986 if( ti = this_interactive() )
987 tin = getuid(this_interactive());
988 verb = query_verb();
989 }
990
991 te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
992
993 // fuer "erwidere"
994 if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
995 if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
996 if (flag & MSGFLAG_WHISPER)
997 add_to_tell_history(getuid(ti), 0, 1,
998 capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
999 (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
1000 ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
1001 " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
1002 else
1003 add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
1004 }
1005 }
1006 // Hoert der Spieler nicht?
1007 em = (ti &&
1008 (te || flag & MSGFLAG_SHOUT) &&
1009 (QueryProp(P_EARMUFFS) &&
1010 (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
1011 ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
1012
1013 // Werden der Sender oder das Verb ignoriert?
1014 if(!ti && tin && flag == MSGFLAG_CHANNEL)
1015 {
1016 if((member(ignore, tin) != -1))
1017 {
1018 return MESSAGE_IGNORE_YOU;
1019 }
1020 if(verb && sizeof(filter(ignore, #'check_ignore, verb, tin)) )
1021 {
1022 return MESSAGE_IGNORE_YOU;
1023 }
1024 }
1025 if (ti && (member(ignore, getuid(ti)) != -1)) {
1026 if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
1027 efun::tell_object(ti, capitalize(name())+
1028 " hoert gar nicht zu, was Du sagst.\n");
1029 return MESSAGE_IGNORE_YOU;
1030 }
1031 if(tin && verb &&
1032 sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
1033 {
1034 if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
1035 verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
1036 efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
1037 return MESSAGE_IGNORE_VERB;
1038 }
1039 if (flag & MSGFLAG_RTELL) {
1040 int at;
1041
1042 verb = lower_case(old_explode(msg, " ")[0][1..]);
1043 at = member(verb, '@');
1044 /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
1045 add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
1046
1047 if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
1048 return MESSAGE_IGNORE_YOU;
1049 else if (at > 0 && member(ignore, verb[at..]) >= 0)
1050 return MESSAGE_IGNORE_MUD;
1051 }
1052
1053 // Taubheit/Oropax
1054 te |= (flag & MSGFLAG_SAY);
1055
1056 if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
1057 deaf = QueryProp(P_DEAF);
1058 if (te)
1059 reply = stringp(deaf) ?
1060 capitalize(sprintf(deaf, name())) :
1061 capitalize(name())+" ist momentan leider taub.\n";
1062 }
1063 else if (em)
1064 reply = capitalize(name())+" hat Oropax in den Ohren.\n";
1065
1066 msg = break_string(msg, 78, indent,
1067 (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
1068
Zesstra7459f252022-02-23 22:47:26 +01001069 if((QueryProp(P_BUFFER) & KOBOLD_ONLINE) &&
MG Mud User88f12472016-06-24 23:31:02 +02001070 (deaf ||
1071 query_editing(this_object()) ||
1072 query_input_pending(this_object())))
1073 {
1074 deaf = MESSAGE_DEAF;
1075 if(flag & MSGFLAG_CACHE)
1076 {
1077 if(!stringp(reply))
1078 reply = name()+" moechte gerade nicht gestoert werden.\n";
1079
1080 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1081
1082 int res = add_to_kobold(msg, 0, 0, 0,
1083 objectp(sender) ? sender : ME);
1084 if(res == MSG_BUFFERED)
1085 {
1086
1087 reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
1088 "genommen.\nEr wird sie spaeter weiterleiten!";
1089 deaf = MESSAGE_CACHE;
1090 }
1091 else {
1092 reply += "Die Mitteilung ging verloren, denn "+
1093 "der Kobold kann sich nichts mehr merken!";
1094 deaf = MESSAGE_CACHE_FULL;
1095 }
1096 if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
1097 efun::tell_object(ti, reply+"\n");
1098 }
1099 return deaf;
1100 }
1101 else if((deaf || em) &&
1102 ( (flag & MSGFLAG_RTELL) ||
1103 (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
1104 if (te && ti)
1105 efun::tell_object(ti, reply);
1106 return MESSAGE_DEAF;
1107 }
1108
1109 _flush_cache(0);
1110 if(te && QueryProp(P_AWAY))
1111 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1112
Zesstra1cc6cf32022-02-09 11:49:31 +01001113 if(!objectp(sender) || sender != ME)
Bugfix60f5bc62022-01-21 11:09:56 +01001114 {
Zesstra1cc6cf32022-02-09 11:49:31 +01001115 if (flag & MSGFLAG_SAY)
1116 comm_beep(MA_SAY);
1117 else if (flag & MSGFLAG_TELL)
1118 comm_beep(MA_TELL);
1119 else if (flag & MSGFLAG_CHANNEL)
1120 comm_beep(MA_CHANNEL);
1121 else if (flag & MSGFLAG_SHOUT)
1122 comm_beep(MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001123 }
1124 efun::tell_object(ME, msg);
1125 return MESSAGE_OK;
1126}
1127
1128static int ignoriere(string str)
1129{
1130 str = _unparsed_args(1);
1131 mapping ignore=Query(P_IGNORE, F_VALUE);
1132
1133 if (!str)
1134 {
1135 string* ignarr = m_indices(ignore);
1136 if (!sizeof(ignarr))
1137 tell_object(ME, "Du ignorierst niemanden.\n");
1138 else
1139 ReceiveMsg("Du ignorierst:\n"
1140 + break_string(CountUp(map(sort_array(ignarr, #'> ),
1141 #'capitalize )
1142 ) + ".",78),
1143 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
1144 0,0,this_object());
1145 return 1;
1146 }
1147 // trim spaces from args and convert to lower case.
1148 str = lower_case(trim(str, TRIM_BOTH));
1149
1150 if (member(ignore, str))
1151 {
1152 RemoveIgnore(str);
1153 tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
1154 }
1155 else if (sizeof(ignore)>100)
1156 {
1157 tell_object(ME, "Du ignorierst schon genuegend!\n");
1158 }
1159 else if (AddIgnore(str) == 1)
1160 {
1161 tell_object(ME,
1162 sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
1163 }
1164 else
1165 {
1166 tell_object(ME,
1167 sprintf("'%s' kannst Du nicht ignorieren.\n",str));
1168 }
1169 return 1;
1170}
1171
1172
Zesstra1cc6cf32022-02-09 11:49:31 +01001173private int _alert_filter_flags(int flag, string text)
Bugfix60f5bc62022-01-21 11:09:56 +01001174{
1175 return (flag & QueryProp(P_ALERT));
1176}
1177
Bugfixc0350dc2025-07-03 14:14:58 +02001178private string beep_status()
1179{
1180 int beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
1181 mapping text = ([
1182 MB_SAY: "sage",
1183 MB_TELL: "teile mit",
1184 MB_CHANNEL: "Ebenenmeldungen",
1185 MB_MENTION: "Erwaehnungen",
1186 MB_SHOUT: "rufe",
1187 MB_MISC: "sonstige",]);
1188 string types = CountUp(map(filter(m_indices(text), #'_alert_filter_flags), text));
1189 if(!sizeof(types))
1190 {
1191 types = "Keine";
1192 }
1193 return
1194 "Gewaehlte Klingeltoene: " + types + "\n"
1195 "Zeitabstand: "
1196 + (beep_interval ? "maximal alle " + beep_interval + " Sekunden."
1197 : "keiner") + "\n"
1198 + (QueryProp(P_ALERT) & AL_NO_SOUND ? " Allerdings sind Toene insgesamt "
1199 "bei Dir abgeschaltet. (\'hilfe ton\')" : "");
1200}
1201
Zesstra1cc6cf32022-02-09 11:49:31 +01001202static int _msg_beep(string str)
1203{
Bugfixc0350dc2025-07-03 14:14:58 +02001204 if(!sizeof(str))
1205 {
1206 ReceiveNotify(
1207 beep_status(),
1208 query_verb());
1209 return 1;
1210 }
Zesstra1cc6cf32022-02-09 11:49:31 +01001211
1212 notify_fail(
1213 "Syntax:\n"
1214 "- klingelton <1 bis 3600 Sekunden>\n"
1215 "- klingelton aus\n"
1216 "- klingelton ein\n"
1217 "- klingelton <+/-><tm / sag / ebenen / ruf / erwaehnung / sonstige / alle / >\n");
1218
Zesstrad536c452025-06-28 14:23:30 +02001219 if (!regmatch(str,"^[[:digit:]]", RE_PCRE))
Bugfix60f5bc62022-01-21 11:09:56 +01001220 {
MG Mud User88f12472016-06-24 23:31:02 +02001221 if (str=="aus")
Zesstra1cc6cf32022-02-09 11:49:31 +01001222 SetProp(P_ALERT, QueryProp(P_ALERT) | AL_NO_SOUND);
1223 else if (str=="ein")
1224 SetProp(P_ALERT, QueryProp(P_ALERT) & ~AL_NO_SOUND);
Bugfix60f5bc62022-01-21 11:09:56 +01001225 else
1226 {
1227 mapping flags = ([
Zesstra1cc6cf32022-02-09 11:49:31 +01001228 "tm": MB_TELL, "teilemit": MB_TELL,
1229 "sag": MB_SAY, "sage": MB_SAY,
1230 "ebene": MB_CHANNEL, "ebenen": MB_CHANNEL,
1231 "ruf": MB_SHOUT, "rufe": MB_SHOUT,
Zesstra3a261e52022-02-10 14:00:31 +01001232 "erwaehnung": MB_MENTION, "erwaehnungen": MB_MENTION,
Zesstra1cc6cf32022-02-09 11:49:31 +01001233 "sonstige": MB_MISC, "sonstiges": MB_MISC,
1234 "alle": MB_ALL, "alles": MB_ALL,
1235 ]);
Bugfix60f5bc62022-01-21 11:09:56 +01001236 foreach(string part : explode(str, " ") - ({""}))
1237 {
Bugfixc0350dc2025-07-03 14:14:58 +02001238 if(!(part[1..] in flags)) return 0;
Bugfix60f5bc62022-01-21 11:09:56 +01001239 if(part[0] == '+')
1240 {
1241 SetProp(P_ALERT,
1242 QueryProp(P_ALERT) | flags[part[1..]]);
1243 }
1244 else if(part[0] == '-')
1245 {
1246 SetProp(P_ALERT,
1247 QueryProp(P_ALERT) & ~ flags[part[1..]]);
1248 }
Bugfixc0350dc2025-07-03 14:14:58 +02001249 else
1250 {
1251 return 0;
1252 }
Bugfix60f5bc62022-01-21 11:09:56 +01001253 }
Bugfix895dc452025-06-30 17:35:45 +02001254 // 0 hat leider historisch die Bedeutung von MB_MISC, daher muss hier
1255 // statt dessen auf AL_NO_SOUND gesetzt werden.
1256 if(!QueryProp(P_ALERT))
1257 {
1258 SetProp(P_ALERT, AL_NO_SOUND);
1259 }
Bugfix60f5bc62022-01-21 11:09:56 +01001260 }
MG Mud User88f12472016-06-24 23:31:02 +02001261 }
Zesstra1cc6cf32022-02-09 11:49:31 +01001262 else
1263 {
Bugfixc0350dc2025-07-03 14:14:58 +02001264 int beep_interval = to_int(str);
Zesstra1cc6cf32022-02-09 11:49:31 +01001265 if(beep_interval >= 0)
1266 {
Bugfixc0350dc2025-07-03 14:14:58 +02001267 SetProp(P_MESSAGE_BEEP, beep_interval);
1268 }
1269 else
1270 {
1271 return 0;
Zesstra1cc6cf32022-02-09 11:49:31 +01001272 }
1273 }
MG Mud User88f12472016-06-24 23:31:02 +02001274
Bugfix60f5bc62022-01-21 11:09:56 +01001275 ReceiveNotify(
Bugfixc0350dc2025-07-03 14:14:58 +02001276 beep_status(),
Bugfix60f5bc62022-01-21 11:09:56 +01001277 query_verb());
MG Mud User88f12472016-06-24 23:31:02 +02001278 return 1;
1279}
1280
1281static int _msg_prepend(string str) {
MG Mud User88f12472016-06-24 23:31:02 +02001282 notify_fail("Syntax: senderwiederholung ein/aus\n");
1283 if (stringp(str)) {
1284 if (str=="aus")
1285 SetProp(P_MESSAGE_PREPEND,1);
1286 else if (str=="ein")
1287 SetProp(P_MESSAGE_PREPEND,0);
1288 else return 0;
1289 }
1290
Bugfixb1d9b4d2021-09-14 20:07:04 +02001291 ReceiveNotify("Senderwiederholung bei Mitteilungen: "+
Zesstra04f613c2019-11-27 23:32:54 +01001292 (({int})QueryProp(P_MESSAGE_PREPEND) ? "aus" : "ein")+".",
MG Mud User88f12472016-06-24 23:31:02 +02001293 query_verb());
1294
1295 return 1;
1296}
1297
1298static int _communicate(mixed str, int silent)
1299{
1300 string verb;
1301 string myname;
1302 string msg;
1303
1304 if (!str || extern_call()) str=_unparsed_args()||"";
1305 /* str=_unparsed_args()||""; */
1306 verb = query_verb();
1307 if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
1308 if (str==""||str==" "||!str)
1309 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001310 ReceiveNotify("Was willst Du sagen?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001311 return 1;
1312 }
1313 msg=permutate(str);
1314
1315 myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
1316 !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
1317 "":QueryProp(P_PRESAY))+name())||"";
1318
1319 // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
1320 // ist, keine MT_COMM)
Zesstrabd1236a2022-02-09 22:35:59 +01001321 send_room(environment(), msg, MT_LISTEN|MSG_ALERT, MA_SAY,
MG Mud User88f12472016-06-24 23:31:02 +02001322 capitalize(myname)+" sagt: ", ({this_object()}) );
1323
1324 if(!silent)
1325 {
1326 ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1327 MA_SAY, "Du sagst: ", ME);
1328 }
1329 return 1;
1330}
1331
1332static int _shout_to_all(mixed str)
1333{
1334 string pre, myname, realname, wizards_msg, players_msg;
1335 string wizard_prefix, player_prefix;
1336 int chars;
1337
1338 if (!(str=_unparsed_args()))
1339 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001340 ReceiveNotify("Was willst Du rufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001341 return 1;
1342 }
1343 chars=sizeof(str)/2;
1344 if (chars<4) chars=4;
1345 pre = (!IS_LEARNER(ME) ||
1346 QueryProp(P_INVIS) ||
1347 !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
1348 realname = capitalize((pre + capitalize(getuid()))||"");
1349 myname = capitalize(pre + name()||"");
1350 if (QueryProp(P_INVIS))
1351 realname = "("+realname+")";
1352
1353 wizards_msg = permutate(str);
1354 wizard_prefix = myname+" ruft: ";
1355
1356 if(QueryProp(P_FROG)) {
1357 players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
1358 player_prefix = myname+" quakt: ";
1359 }
1360 else {
1361 players_msg = wizards_msg;
1362 player_prefix = wizard_prefix;
1363 }
1364
1365 if(!IS_LEARNER(this_player()))
1366 {
1367 if(QueryProp(P_GHOST)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001368 ReceiveNotify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
MG Mud User88f12472016-06-24 23:31:02 +02001369 MA_SHOUT);
1370 return 1;
1371 }
1372 if (QueryProp(P_SP) <(chars+20))
1373 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001374 ReceiveNotify("Du musst erst wieder magische Kraefte sammeln.",
MG Mud User88f12472016-06-24 23:31:02 +02001375 MA_SHOUT);
Bugfixb1d9b4d2021-09-14 20:07:04 +02001376 ReceiveNotify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
MG Mud User88f12472016-06-24 23:31:02 +02001377 "Ebenen').", MA_SHOUT);
1378 return 1;
1379 }
1380 SetProp(P_SP, QueryProp(P_SP) - chars - 20);
1381 }
1382
1383 ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
Zesstrabd1236a2022-02-09 22:35:59 +01001384 MA_SHOUT, "Du rufst: ", ME);
MG Mud User88f12472016-06-24 23:31:02 +02001385
1386 foreach ( object ob : users()-({this_object()}) )
1387 if ( IS_LEARNER(ob) )
Zesstrabd1236a2022-02-09 22:35:59 +01001388 ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR|MSG_ALERT, MA_SHOUT,
1389 wizard_prefix, this_object());
MG Mud User88f12472016-06-24 23:31:02 +02001390 else
Zesstrabd1236a2022-02-09 22:35:59 +01001391 ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR|MSG_ALERT, MA_SHOUT,
1392 player_prefix, this_object());
MG Mud User88f12472016-06-24 23:31:02 +02001393
1394 return 1;
1395}
1396
1397varargs int _tell(string who, mixed msg)
1398{
1399 object ob;
1400 string away,myname,ret;
MG Mud User88f12472016-06-24 23:31:02 +02001401 string *xname;
1402 int i,visflag;
1403
1404 if (extern_call() && this_interactive()!=ME) return 1;
1405 if (!who || !msg) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001406 ReceiveNotify("Was willst Du mitteilen?",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001407 return 1;
1408 }
1409
1410 if(who == ERWIDER_PARAM)
1411 {
1412 if (!last_comm_partner)
1413 {
1414 _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
1415 "Du was erwidern\nkoenntest.\n");
1416 return 0;
1417 }
1418 who=last_comm_partner;
1419 }
1420
1421 // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
1422 if (who == ".")
1423 who = ".1";
1424
1425 if ( sscanf(who, ".%d", i) == 1 ) {
1426 if(i > 0 && i <= sizeof(commreceivers))
1427 who = commreceivers[i-1];
1428 else {
1429 _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
1430 return 0;
1431 }
1432 }
1433
1434 xname = explode(who, "@");
1435
Zesstrabd1236a2022-02-09 22:35:59 +01001436 if (sizeof(xname) == 2)
MG Mud User88f12472016-06-24 23:31:02 +02001437 {
1438 if ( QueryProp(P_QP) )
1439 {
Zesstra04f613c2019-11-27 23:32:54 +01001440 if (ret=({string})INETD->_send_udp(xname[1],
MG Mud User88f12472016-06-24 23:31:02 +02001441 ([ REQUEST: "tell",
1442 RECIPIENT: xname[0],
1443 SENDER: getuid(ME),
1444 DATA: msg ]), 1))
1445 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001446 ReceiveNotify(ret, MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001447 }
1448 else
1449 {
1450 write("Nachricht abgeschickt.\n");
1451 add_to_tell_history(who, 1, 0, msg,
Zesstraa31cd5c2016-12-17 20:11:07 +01001452 "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_RTELL, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001453 }
1454 }
1455 else
1456 write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
1457 "Muds etwas mitteilen zu koennen.\n");
1458 return 1;
1459 }
1460
Zesstra8b5320e2022-02-18 21:10:26 +01001461 string|int lname = lower_case(who);
1462 if (!ob=find_player(lname))
MG Mud User88f12472016-06-24 23:31:02 +02001463 {
Zesstra8b5320e2022-02-18 21:10:26 +01001464 lname = match_living(lname, 0);
1465 if (!stringp(lname))
1466 {
1467 switch(lname)
1468 {
MG Mud User88f12472016-06-24 23:31:02 +02001469 case -1:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001470 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001471 return 1;
1472 case -2:
Zesstra8b5320e2022-02-18 21:10:26 +01001473 // check KOBOLD
Zesstra7459f252022-02-23 22:47:26 +01001474 ob = load_object(KOBOLD);
Zesstra8b5320e2022-02-18 21:10:26 +01001475 lname = ({string|int})ob->find_player(lower_case(who));
1476 if (lname == -1) {
1477 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
1478 return 1;
1479 }
1480 else if (!stringp(lname)) {
1481 ReceiveNotify("Kein solcher Spieler (online)!",MA_TELL);
1482 return 1;
1483 }
MG Mud User88f12472016-06-24 23:31:02 +02001484 }
Zesstra8b5320e2022-02-18 21:10:26 +01001485 // jetzt ist ob der KOBOLD und lname die UID eines Spielers.
1486 }
1487 // Wenn wir noch kein ob haben, sollte lname jetzt aber durch das
1488 // match_living() der Name eines Livings sein, dessen Objekt wir noch
1489 // brauchen.
1490 if (!ob)
1491 ob = find_player(lname) || find_living(lname);
MG Mud User88f12472016-06-24 23:31:02 +02001492 if(!ob) {
Zesstra8b5320e2022-02-18 21:10:26 +01001493 ReceiveNotify("Kein solcher Spieler (online)!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001494 return 1;
1495 }
1496 }
1497
Zesstra8b5320e2022-02-18 21:10:26 +01001498 if(QueryProp(P_INVIS))
1499 {
MG Mud User88f12472016-06-24 23:31:02 +02001500 if(!IS_LEARNER(ob))
1501 myname = name();
1502 else
1503 myname="("+
1504 ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
1505 capitalize(getuid()) + ")";
1506 }
1507 else
1508 myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
Bugfix45f88ce2017-03-06 14:41:56 +01001509 QueryProp(P_PRESAY):"") + capitalize(getuid(ME));
Zesstra8b5320e2022-02-18 21:10:26 +01001510 if (myname && sizeof(myname))
1511 myname=capitalize(myname);
1512
1513 // Wir haben Zielobjekt und unseren eigenen Namen. Erstmal an Empfaenger
1514 // senden.
1515 if (living(ob))
1516 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_ALERT, MA_TELL,
1517 myname + " teilt Dir mit: ");
1518 else
1519 _send_to_kobold(lname, permutate(msg), MT_COMM|MT_FAR|MSG_ALERT, MA_TELL,
1520 myname + " teilt Dir mit: ");
MG Mud User88f12472016-06-24 23:31:02 +02001521
1522 // dann evtl. noch an Absender ausgeben...
Zesstra8b5320e2022-02-18 21:10:26 +01001523 visflag = !ob->QueryProp(P_INVIS);
1524 if (visflag || IS_LEARNER(this_player()))
1525 _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(lname) + " mit: ");
MG Mud User88f12472016-06-24 23:31:02 +02001526 // oder irgendwas anderes an den Absender ausgeben...
1527 if (!visflag && interactive(ob))
Zesstra8b5320e2022-02-18 21:10:26 +01001528 ReceiveNotify("Kein solcher Spieler online!",MA_TELL);
Zesstra04f613c2019-11-27 23:32:54 +01001529 else if (away = ({string})ob->QueryProp(P_AWAY))
Zesstra8b5320e2022-02-18 21:10:26 +01001530 ReceiveMsg( break_string( away, 78, capitalize(lname)
MG Mud User88f12472016-06-24 23:31:02 +02001531 + " ist gerade nicht da: ", BS_INDENT_ONCE ),
1532 MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
1533 MA_TELL, 0, this_object());
1534 else if (interactive(ob) && (i=query_idle(ob))>=600)
1535 { //ab 10 Mins
1536 if (i<3600)
1537 away=time2string("%m %M",i);
1538 else
1539 away=time2string("%h %H und %m %M",i);
1540
Bugfixb1d9b4d2021-09-14 20:07:04 +02001541 ReceiveNotify(sprintf("%s ist seit %s voellig untaetig.",
Zesstra8b5320e2022-02-18 21:10:26 +01001542 capitalize(lname),away),
MG Mud User88f12472016-06-24 23:31:02 +02001543 MA_TELL);
1544 }
1545
1546 return 1;
1547}
1548
1549static int _teile(string str)
1550{
1551 string who, message;
1552 if (!(str=_unparsed_args())) return 0;
1553 if (sscanf(str, "%s mit %s", who, message) == 2)
1554 return _tell(who, message,1);
1555 return 0;
1556}
1557static int _teile_mit_alias(string str)
1558{
1559 str = _unparsed_args(), TRIM_LEFT;
1560 if (!str) return 0;
1561 str = trim(str, TRIM_LEFT);
1562 // Ziel muss min. 2 Buchstaben haben (.<nr>)
1563 if (sizeof(str) < 4) return 0;
1564 int pos = strstr(str, " ");
1565 if (pos >= 2)
1566 return _tell(str[..pos-1], str[pos+1..]);
1567 return 0;
1568}
1569
1570static int _erzaehle(string str)
1571{
1572 string who, message;
1573
1574 if (!(str=_unparsed_args())) return 0;
1575 if (sscanf(str, "%s %s", who, message) == 2)
1576 return _tell(who, message,1);
1577 return 0;
1578}
1579
1580static int _whisper(string str)
1581{
1582 object ob;
1583 string who;
1584 string msg;
1585 string myname;
1586
1587 if (!(str=_unparsed_args()) ||
1588 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1589 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001590 ReceiveNotify("Was willst Du wem zufluestern?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001591 return 1;
1592 }
Zesstra6e88b6a2019-11-08 00:25:39 +01001593 who = lower_case(who);
MG Mud User88f12472016-06-24 23:31:02 +02001594 if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001595 ReceiveNotify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001596 return 1;
1597 }
1598
1599 myname = capitalize((((IS_LEARNER(ME) &&
1600 !QueryProp(P_INVIS) &&
1601 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1602 QueryProp(P_PRESAY) : "") + name()) || "");
1603
1604 _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
Bugfixdbdbed52022-01-21 11:14:35 +01001605 MA_SAY, myname + " fluestert Dir zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001606 send_room(environment(),
1607 myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
1608 MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
1609
1610 _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
1611
1612
1613 return 1;
1614}
1615
1616static int _remote_whisper(string str)
1617{
1618 /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
1619 Nachricht nicht in der tell_history verewigt. */
1620
1621 object ob;
Zesstra8b5320e2022-02-18 21:10:26 +01001622 string who;
MG Mud User88f12472016-06-24 23:31:02 +02001623 string msg;
1624 string myname;
1625
1626 if (!(str=_unparsed_args()) ||
1627 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1628 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001629 ReceiveNotify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001630 return 1;
1631 }
1632
Zesstra8b5320e2022-02-18 21:10:26 +01001633 string|int lname = lower_case(who);
1634 if (!ob=find_player(lname))
MG Mud User88f12472016-06-24 23:31:02 +02001635 {
Zesstra8b5320e2022-02-18 21:10:26 +01001636 lname = match_living(lname, 0);
1637 if (!stringp(lname))
1638 {
1639 switch(lname)
1640 {
MG Mud User88f12472016-06-24 23:31:02 +02001641 case -1:
Zesstra8b5320e2022-02-18 21:10:26 +01001642 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001643 return 1;
1644 case -2:
Zesstra8b5320e2022-02-18 21:10:26 +01001645 // rfluester benutzt keinen KOBOLD.
1646 ReceiveNotify("Kein solcher Spieler!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001647 return 1;
1648 }
Zesstra8b5320e2022-02-18 21:10:26 +01001649 }
1650 // lname sollte nun eindeutig sein.
1651 ob = find_player(lname) || find_living(lname);
1652 if(!ob) {
1653 ReceiveNotify("Kein solcher Spieler online!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001654 return 1;
1655 }
1656 }
Zesstra8b5320e2022-02-18 21:10:26 +01001657
MG Mud User88f12472016-06-24 23:31:02 +02001658 if (environment(ob) == environment()) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001659 ReceiveNotify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001660 return 1;
1661 }
1662
1663 myname = capitalize((((IS_LEARNER(ME) &&
1664 !QueryProp(P_INVIS) &&
1665 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1666 QueryProp(P_PRESAY) : "") + name()) || "");
1667
1668 // An Empfaenger senden.
Zesstrabd1236a2022-02-09 22:35:59 +01001669 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE,
1670 MA_EMOTE, myname + " fluestert Dir aus der Ferne zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001671 // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
1672 if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
Zesstra8b5320e2022-02-18 21:10:26 +01001673 ReceiveNotify("Kein solcher Spieler online!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001674 return 1;
1675 }
1676 // sonst eigene Meldung via _recv() ausgeben.
1677 else
1678 _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
1679 "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
1680
1681 return 1;
1682}
1683
1684static int _converse(string arg)
1685{
Bugfixb1d9b4d2021-09-14 20:07:04 +02001686 ReceiveNotify("Mit '**' wird das Gespraech beendet.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001687 if (stringp(arg) && strstr(arg, "-s") == 0)
1688 input_to("_converse_more", INPUT_PROMPT, "]", 1);
1689 else
1690 input_to("_converse_more", INPUT_PROMPT, "]", 0);
1691 return 1;
1692}
1693
1694static int _converse_more(mixed str, int silent)
1695{
1696 if (str == "**") {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001697 ReceiveNotify("Ok.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001698 return 0;
1699 }
1700
1701 if(str != "")
1702 _communicate(str, silent);
1703
1704 input_to("_converse_more", INPUT_PROMPT, "]", silent);
1705 return 1;
1706}
1707
1708private int is_learner(object o) { return IS_LEARNER(o); }
1709
1710static int _shout_to_wizards(mixed str)
1711{
MG Mud User88f12472016-06-24 23:31:02 +02001712 string myname;
MG Mud User88f12472016-06-24 23:31:02 +02001713
1714 str = _unparsed_args();
1715 if (!str||!sizeof(str)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001716 ReceiveNotify("Was willst Du den Magiern zurufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001717 return 1;
1718 }
1719 // Kontrollzeichen rausfiltern.
1720 str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
1721 myname = capitalize(getuid(this_object()));
1722 if (!IS_LEARNER(this_object()))
1723 _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
1724
1725 // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
1726 filter(users(), #'is_learner)->ReceiveMsg(str,
Zesstrabd1236a2022-02-09 22:35:59 +01001727 MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_ALERT,
MG Mud User88f12472016-06-24 23:31:02 +02001728 MA_SHOUT, myname+" an alle Magier: ", this_object());
1729
1730 return 1;
1731}
1732
1733static int _echo(string str) {
1734 if (!IS_SEER(ME) || (!IS_LEARNER(ME)
1735 && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
1736 return 0;
1737
1738 if (!(str=_unparsed_args())) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001739 ReceiveNotify("Was moechtest Du 'echoen'?", 0);
MG Mud User88f12472016-06-24 23:31:02 +02001740 return 1;
1741 }
1742
1743 if (!IS_LEARNER(this_interactive()))
1744 {
1745 if (QueryProp(P_GHOST))
1746 {
1747 _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
1748 return 0;
1749 }
1750 if (QueryProp(P_SP)<ECHO_COST)
1751 {
1752 _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
1753 return 0;
1754 }
1755 SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
1756 str=">\b"+str;
1757 log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
1758 str));
1759 }
1760 // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
1761 // Kommhistory noch im Kobold landen.
1762 send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
1763 MA_UNKNOWN, 0, 0);
1764 return 1;
1765}
1766
1767// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
1768// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
1769// und gefedert.
1770static string *_set_ignore(mixed arg)
1771{
1772 raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
1773 "Benutze AddIgnore/RemoveIgnore()!\n");
1774}
1775// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
1776// nix dran, dass alle TestIgnore() & Co benutzen sollen.
1777static string *_query_ignore() {
1778 mixed ign=Query(P_IGNORE, F_VALUE);
1779 if (mappingp(ign))
1780 return m_indices(ign);
1781 return ({});
1782}
1783
1784public int AddIgnore(string ign) {
1785 // Einige strings sind nicht erlaubt, z.B. konsekutive .
1786 if (!sizeof(ign)
1787 || regmatch(ign,"[.]{2,}",RE_PCRE)
1788 || regmatch(ign," ",RE_PCRE)
1789 || sizeof(explode(ign,"."))>3)
1790 return 0;
1791
1792 mapping ignores=Query(P_IGNORE, F_VALUE);
1793 ignores[ign]=time();
1794 // kein Set() noetig.
1795 return 1;
1796}
1797
1798public int RemoveIgnore(string ign)
1799{
1800 mapping ignores=Query(P_IGNORE,F_VALUE);
1801 m_delete(ignores,ign);
1802 // Kein Set() noetig
1803 return 1;
1804}
1805
1806static int _query_intermud()
1807{
1808 mixed tmp;
1809 return member(pointerp(tmp=Query(P_CHANNELS))?tmp:({}), "Intermud") > -1;
1810}
1811
1812
1813int erwidere(string str)
1814{
1815 str=_unparsed_args();
1816 if (!str) return 0;
1817 return _tell(ERWIDER_PARAM, str ,1);
1818}
1819
1820static int tmhist(string str)
1821{
1822
1823 if (str == "aus") {
1824 tell_history_enabled = TELLHIST_DISABLED;
1825 write("Ok, es wird nichts mehr gespeichert.\n");
1826 if (sizeof(tell_history)) {
Zesstra996bafe2022-02-14 22:42:39 +01001827 clear_tell_history(1);
MG Mud User88f12472016-06-24 23:31:02 +02001828 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1829 }
1830 return 1;
1831 }
Zesstra996bafe2022-02-14 22:42:39 +01001832 if (str == "loeschen")
1833 {
1834 if (sizeof(tell_history)) {
1835 clear_tell_history(1);
1836 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1837 }
1838 else
1839 write("Deine Mitteilungsgeschichte war schon leer.\n");
1840 return 1;
1841 }
MG Mud User88f12472016-06-24 23:31:02 +02001842
1843 if (str == "namen") {
1844 int flag;
1845 tell_history_enabled = TELLHIST_NO_MESSAGE;
1846 write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
1847 foreach (string uid, struct chat_s chat: tell_history)
1848 if (pointerp(chat->msgbuf)) {
1849 chat->msgbuf = 0;
1850 chat->ptr = 0;
1851 flag = 1;
1852 }
1853 if (flag)
1854 write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
1855 return 1;
1856 }
1857
1858 if (str == "ein" || str == "an") {
1859 tell_history_enabled = TELLHIST_ENABLED;
1860 write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
1861 return 1;
1862 }
1863
MG Mud User88f12472016-06-24 23:31:02 +02001864 if (str == "langlebig") {
1865 tell_history_enabled = TELLHIST_LONGLIFE;
1866 write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
1867 "Ende/Crash/\nReboot gespeichert.\n");
1868 return 1;
1869 }
MG Mud User88f12472016-06-24 23:31:02 +02001870
1871 if (str == "status") {
1872 switch (tell_history_enabled) {
1873 case TELLHIST_DISABLED:
1874 write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
1875 break;
1876 case TELLHIST_NO_MESSAGE:
1877 write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
1878 break;
1879 case TELLHIST_ENABLED:
1880 write("Deine Mitteilungen werden gespeichert.\n");
1881 break;
MG Mud User88f12472016-06-24 23:31:02 +02001882 case TELLHIST_LONGLIFE:
1883 write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
1884 "Crash/Reboot\ngespeichert.\n");
1885 break;
MG Mud User88f12472016-06-24 23:31:02 +02001886 }
1887 return 1;
1888 }
1889
1890 if (tell_history_enabled == TELLHIST_DISABLED) {
1891 _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
1892 return 0;
1893 }
1894
1895 if (!sizeof(tell_history)) {
1896 _notify_fail("Du hast noch keinem etwas mitgeteilt "
1897 "und noch keine Mitteilungen erhalten.\n");
1898 return 0;
1899 }
1900
1901 if (str && sizeof(str)) {
1902
1903 if (tell_history_enabled < TELLHIST_ENABLED) {
1904 _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
1905 return 0;
1906 }
1907
1908 string uid;
1909 if (member(tell_history, str)) {
1910 // Name gewuenscht, da der String in der History vorkommt.
1911 uid = str;
1912 }
1913 else {
1914 // evtl. ne Zahl angegeben.
1915 int i;
1916 string *partners = sorted_commpartners(0);
1917 if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
1918 uid = partners[i];
1919 else {
1920 notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
1921 return 0;
1922 }
1923 }
1924
1925 mixed *data = tell_history[uid]->msgbuf;
1926 if (!data) {
1927 _notify_fail(
1928 "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
1929 return 0;
1930 }
1931
1932 int ptr = tell_history[uid]->ptr;
1933
1934 More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
1935 data[0..ptr-1],
Zesstra7459f252022-02-23 22:47:26 +01001936 function string (struct kobold_msg_s msg) {
MG Mud User88f12472016-06-24 23:31:02 +02001937 if (!structp(msg)) return "";
Zesstra3a261e52022-02-10 14:00:31 +01001938 return break_string(terminal_colour(msg->msg, colourmap)
Zesstra7459f252022-02-23 22:47:26 +01001939 + " ["
1940 + strftime("%H:%M:%S",msg->timestamp) + "]", 78,
MG Mud User88f12472016-06-24 23:31:02 +02001941 msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
1942 } ) ) );
1943 return 1;
1944 }
1945
1946 string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
1947 int i;
1948 foreach (string uid : sorted_commpartners(0) ) {
1949 int j;
1950 struct chat_s chat = tell_history[uid];
1951 history += sprintf("%2d.%-4s %s %-11s %d gesendet/%d empfangen\n", ++i,
1952 ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
1953 strftime("%a, %e.%m.%y",chat->time_last_msg),
1954 capitalize(chat->uid), chat->sentcount, chat->recvcount);
1955 }
1956
1957 More(history);
1958
1959 return 1;
1960}
1961
1962static mixed _query_localcmds()
1963{
1964 return ({
1965 ({"kobold", "cmd_kobold",0,0}),
1966 ({"sag","_communicate",0,0}),
1967 ({"sage","_communicate",0,0}),
1968 ({"'","_communicate",1,0}),
1969 ({"mruf","_shout_to_wizards",0,0}),
1970 ({"mrufe","_shout_to_wizards",0,0}),
1971 ({"ruf","_shout_to_all",0,0}),
1972 ({"rufe","_shout_to_all",0,0}),
1973 ({"erzaehl","_erzaehle",0,0}),
1974 ({"erzaehle","_erzaehle",0,0}),
1975 ({"teil","_teile",0,0}),
1976 ({"teile","_teile",0,0}),
1977 ({"tm","_teile_mit_alias",0,0}),
1978 ({"fluester","_whisper",0,0}),
1979 ({"fluestere","_whisper",0,0}),
1980 ({"rfluester","_remote_whisper",0,0}),
1981 ({"rfluestere","_remote_whisper",0,0}),
1982 ({"gespraech","_converse",0,0}),
1983 ({"echo","_echo",0,0}),
1984 ({"ignorier","ignoriere",0,0}),
1985 ({"ignoriere","ignoriere",0,0}),
1986 ({"tmhist","tmhist",0,0}),
1987 ({"erwider","erwidere",0,0}),
1988 ({"erwidere","erwidere",0,0}),
1989 ({"klingelton","_msg_beep",0,0}),
1990 ({"senderwiederholung","_msg_prepend",0,0}),
1991 ({"report","set_report",0,0}),
1992 })+channel::_query_localcmds();
1993}
1994
1995private string *sorted_commpartners(int reversed) {
1996 return sort_array(m_indices(tell_history),
1997 function int (string uid1, string uid2) {
1998 if (reversed)
1999 return tell_history[uid1]->time_last_msg >
2000 tell_history[uid2]->time_last_msg;
2001 else
2002 return tell_history[uid1]->time_last_msg <=
2003 tell_history[uid2]->time_last_msg;
2004 } );
2005}
2006
Zesstra0712bac2020-06-12 09:41:24 +02002007// Setzt den Prompt eines Interactives. Gerufen bei Objekterstellung,
2008// Reconnect und bei Magiern, wenn diese ihren Prompt oder ihr aktuelles
2009// Verzeichnis aendern.
MG Mud User88f12472016-06-24 23:31:02 +02002010static void modify_prompt() {
2011 string text = Query(P_PROMPT, F_VALUE);
2012
2013 if ( !stringp(text) || !sizeof(text) )
2014 text = "> ";
2015 else {
2016 string path = Query(P_CURRENTDIR, F_VALUE);
2017 if (stringp(path) && sizeof(path))
2018 text = regreplace(text,"\\w",path,0); // Pfad einsetzen
2019 }
2020 configure_interactive(this_object(), IC_PROMPT, text);
2021}
2022
2023// Prueft auf Ingoriereeintraege.
2024// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
MG Mud User88f12472016-06-24 23:31:02 +02002025public int TestIgnore(string|string* srcnames)
MG Mud User88f12472016-06-24 23:31:02 +02002026{
2027 mapping ign = Query(P_IGNORE, F_VALUE);
2028 if (stringp(srcnames))
2029 srcnames = ({srcnames});
2030
2031 foreach(string srcname: srcnames)
2032 {
2033 // einfachster Fall, exakter Match
2034 if (member(ign, srcname))
2035 return MSG_IGNORED;
2036 // ansonsten muss aufgetrennt werden.
2037 if (strstr(srcname,".") > -1)
2038 {
2039 string *srcparts=explode(srcname,".");
2040 switch(sizeof(srcparts))
2041 {
2042 // case 0 und 1 kann nicht passieren.
2043 case 3:
2044 // zu pruefen: [sender].aktion.qualifizierer.
2045 // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
2046 // oben schon geprueft. im Spieler geprueft werden muss noch:
2047 // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
2048 if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
2049 || member(ign, "."+srcparts[1]) // .aktion
2050 || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
2051 || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
2052 )
2053 {
2054 return MSG_IGNORED;
2055 }
2056 break;
2057 case 2:
2058 // zu pruefen: spieler.aktion
2059 // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
2060 // oben schon geprueft. Im Spieler zu pruefen ist noch:
2061 // spieler und .aktion
2062 if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
2063 || member(ign, "."+srcparts[1]))
2064 {
2065 return MSG_IGNORED;
2066 }
2067 break;
2068 default: // mehr als 3 Teile...
2069 raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
2070 "is supported. Got: %s\n",srcname));
MG Mud User88f12472016-06-24 23:31:02 +02002071 }
2072 }
2073 }
2074 // Default: nicht ignorieren.
2075 return 0;
2076}
2077
2078#ifdef __LPC_UNIONS__
2079public int TestIgnoreExt(string|string* srcnames)
2080#else
2081public int TestIgnoreExt(mixed srcnames)
2082#endif
2083{
2084 return TestIgnore(srcnames);
2085}
2086
2087// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
2088// Typen.
2089// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
2090private int check_ignores(string msg, int msg_type, string msg_action,
Zesstra7459f252022-02-23 22:47:26 +01002091 string msg_prefix, object|string origin)
MG Mud User88f12472016-06-24 23:31:02 +02002092{
2093 // Einige Dinge lassen sich nicht ignorieren.
2094 if (msg_type & (MT_NEWS|MT_NOTIFICATION))
2095 return 0;
2096 // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
2097 // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
2098 // eine ignorierbare msg_action geben.
2099 else if (stringp(msg_action) && origin && origin != ME)
2100 {
Zesstra7459f252022-02-23 22:47:26 +01002101 string srcname;
2102 if (objectp(origin))
2103 srcname =
MG Mud User88f12472016-06-24 23:31:02 +02002104 (query_once_interactive(origin) ? origin->query_real_name()
2105 : origin->name(WER) || "");
Zesstra7459f252022-02-23 22:47:26 +01002106 else
2107 srcname = origin;
2108
MG Mud User88f12472016-06-24 23:31:02 +02002109 mapping ign = Query(P_IGNORE, F_VALUE);
2110
2111 if (member(ign, srcname))
2112 return MSG_IGNORED;
2113 // vielleicht wird irgendwas a la name.aktion ignoriert?
2114 // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
2115 // Ebenenmeldungen)
2116 if (member(ign, srcname+"."+msg_action))
2117 return MSG_VERB_IGN;
2118 // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
2119 // das reichlich sinnfrei ist.
2120 if (member(ign, "."+msg_action))
2121 return MSG_VERB_IGN;
2122 // Spieler auf Ebenen ignoriert?
2123 // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
2124 if (strstr(msg_action, MA_CHANNEL) == 0)
2125 {
2126 // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
2127 if (member(ign, srcname + "."MA_CHANNEL))
2128 return MSG_IGNORED;
2129 // spieler.ebenen.ebenenname ist oben schon abgedeckt.
2130 // .ebenen halte ich fuer sinnfrei, nicht geprueft.
2131 }
2132 // Spieler aus anderem mud? *seufz*
2133 if (strstr(srcname,"@") > -1)
2134 {
2135 string *srcparts = explode(srcname,"@");
2136 if (sizeof(srcparts)==2)
2137 {
2138 // spieler@?
2139 if (member(ign, srcparts[0]+"@"))
2140 return MSG_IGNORED;
2141 // oder Mud per @mud?
2142 if (member(ign, "@" + srcparts[1]))
2143 return MSG_MUD_IGN;
2144 // BTW: spieler@mud wurde schon ganz oben erfasst.
2145 }
2146 }
2147 }
2148 // Default: nicht ignorieren.
2149 return 0;
2150}
2151
2152// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
2153// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
Bugfixe0fc68f2022-01-07 15:30:54 +01002154// BLindheit/Taubheit). In Zukunft koennten aber weitere Typen und weitere
2155// Kriterien wichtig werden und weitere Argumente uebergeben werden. (Dies
2156// bitte beachten, falls diese Funktion protected/public werden sollte.)
MG Mud User88f12472016-06-24 23:31:02 +02002157// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
2158// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
2159// Sinn gar nix zu tun hat.
2160// Rueckgabe: 0 oder MSG_SENSE_BLOCK
Bugfixe0fc68f2022-01-07 15:30:54 +01002161private int check_senses(int msg_type)
MG Mud User88f12472016-06-24 23:31:02 +02002162{
2163 int senses = msg_type & (MT_LOOK|MT_LISTEN);
2164 // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
2165 // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
2166 // unterdrueckt werden.
2167 if (!senses)
2168 return 0;
Bugfix427a5812022-01-07 15:41:24 +01002169
2170 int orig_senses = senses;
2171
MG Mud User88f12472016-06-24 23:31:02 +02002172 if ((senses & MT_LOOK) && CannotSee(1))
2173 senses &= ~MT_LOOK; // Sinn loeschen
2174
2175 if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
2176 senses &= ~MT_LISTEN;
2177
Bugfix427a5812022-01-07 15:41:24 +01002178 // Wenn kein Sinn mehr ueber ist oder all_types gesetzt ist und nicht mehr
2179 // alle Sinne vorhanden sind, wird die Nachricht nicht wahrgenommen.
2180 if (orig_senses == senses // kein Sinn geloescht, haeufigster Fall
2181 || (!(msg_type&MSG_ALL_SENSES) && senses) ) // min. ein Sinn uebrig
2182 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02002183
Bugfix427a5812022-01-07 15:41:24 +01002184 return MSG_SENSE_BLOCK;
MG Mud User88f12472016-06-24 23:31:02 +02002185}
2186
Zesstra09bcf042023-12-30 20:07:19 +01002187public int ReceiveMsg(string msg, int msg_type, string msg_action,
MG Mud User88f12472016-06-24 23:31:02 +02002188 string msg_prefix, object origin)
2189{
2190 if (!msg) return MSG_FAILED;
2191
2192 // Flags und Typen spalten
2193 int flags = msg_type & MSG_ALL_FLAGS;
2194 int type = msg_type & ~flags;
2195
Zesstra09bcf042023-12-30 20:07:19 +01002196 // msg_action kommt per Defaultargument, msg_type hat per Default 0, daher
2197 // ggf. auch noch den Typ raten (leider geht msg_type nicht als
2198 // Defaultargument, da es *nach* msg_action ausgewertet werden muss, aber in
2199 // der Argumentliste leider *vor* msg_action kommt...
MG Mud User88f12472016-06-24 23:31:02 +02002200 type ||= comm_guess_message_type(msg_action, origin);
2201
2202 // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
2203 if (msg_type & MT_DEBUG)
2204 {
2205 if (!QueryProp(P_WIZ_DEBUG)
2206 || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
2207 return MSG_FAILED;
2208 }
2209
2210 // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
2211 // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
2212 // Weiterbearbeitung.
2213 // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
2214 if (!(flags & MSG_DONT_IGNORE))
2215 {
Bugfix427a5812022-01-07 15:41:24 +01002216 // Sinne pruefen.
2217 int res=check_senses(msg_type);
MG Mud User88f12472016-06-24 23:31:02 +02002218 if (res) return res;
2219
2220 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
2221 res=check_ignores(msg, type, msg_action, msg_prefix, origin);
2222 if (res) return res;
2223 }
2224
Zesstra3a261e52022-02-10 14:00:31 +01002225 // Mentions in der Form @Charname werden in Kommunikation oder beim Hoeren
2226 // von Rufen und Sagen markiert und gegebenfalls gibt es ein Pieps dafuer
2227 int mention;
2228 if ((type & MT_COMM)
2229 || ((type & MT_LISTEN) && msg_action in ({MA_SAY, MA_SHOUT}))
2230 )
2231 {
2232 // Um den Charnamen Tags fuer terminal_colour() einfuegen.
2233 // Lookahead und Lookbehind assertions um die Whitespaces um das Wort
2234 // nicht in den Match einzuschliessen (und zu ersetzen).
2235 string tmp = regreplace(msg,
Zesstra88cf8172025-06-28 13:19:53 +02002236 sprintf("(?<=\\s){0,1}@%s(?=\\s*){0,1}",getuid(ME)),
Zesstra3a261e52022-02-10 14:00:31 +01002237 sprintf("%%^mention%%^@%s%%^normal%%^",
2238 capitalize(getuid(ME))),
2239 RE_PCRE|RE_GLOBAL|RE_CASELESS);
2240 send_debug(ME, tmp);
2241 // Der Vergleich ist weniger schlimm als es aussieht, weil die Strings
2242 // unterschiedlich gross sein werden und daher nicht zeichenweise
2243 // verglichen werden muessen. Es ist dann jedenfalls schneller als
2244 // getrennt mit regmatch oder strstr zu schauen, ob @charname
2245 // vorkommt.
2246 if (tmp != msg)
2247 {
2248 msg = tmp;
2249 mention = 1;
2250 }
2251 }
2252
MG Mud User88f12472016-06-24 23:31:02 +02002253 // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
2254 if ((type & MT_COMM))
2255 {
2256 // erstmal in der Komm-History ablegen, wenn gewuenscht.
2257 if ((!(flags & MSG_DONT_STORE)))
2258 {
2259 string uid;
2260 if (query_once_interactive(origin))
2261 uid = origin->query_real_name();
2262 else
2263 uid = origin->name(WER) || "<unbekannt>";
Zesstraa31cd5c2016-12-17 20:11:07 +01002264 add_to_tell_history(uid, 0, 1, msg, msg_prefix,
2265 (msg_action == MA_TELL ? MSGFLAG_TELL : 0 ) );
MG Mud User88f12472016-06-24 23:31:02 +02002266 }
2267
2268 // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
2269 // Ebenenmeldungen. (Die haben ggf. schon.)
2270 if (stringp(msg_action) && QueryProp(P_AWAY)
2271 && strstr(msg_action, MA_CHANNEL) != 0)
2272 {
2273 // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
2274 if (msg[<1] == '\n')
2275 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
2276 else
2277 msg = msg + " [" + strftime("%H:%M",time()) + "]";
2278 }
2279 // Kobold erlaubt und gewuenscht? Kobold ist fuer die
2280 // direkte Kommunikation mittels MT_COMM vorgesehen.
2281 // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
2282 // ignorieren.
2283 // die if-Konstruktion ist so, weil ich das _flush_cache() im else
2284 // brauche.
2285 if (query_editing(this_object()) || query_input_pending(this_object())
2286 || QueryProp(P_EARMUFFS))
2287 {
2288 if (!(flags & MSG_DONT_BUFFER)
Zesstra7459f252022-02-23 22:47:26 +01002289 && (QueryProp(P_BUFFER) & KOBOLD_ONLINE))
MG Mud User88f12472016-06-24 23:31:02 +02002290 {
2291 // Nachricht soll im Kobold gespeichert werden.
2292 return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
2293 }
2294 }
2295 else
2296 {
2297 // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
2298 // entleeren.
2299 _flush_cache(0);
2300 }
Bugfix3afcb792022-01-21 22:32:42 +01002301 }
2302
Zesstra3a261e52022-02-10 14:00:31 +01002303 // Farbtags ersetzen
2304 msg = terminal_colour(msg, colourmap);
2305
2306 // Alertton senden?
2307 if ((msg_type & MSG_ALERT) || mention)
2308 comm_beep(msg_action, mention);
MG Mud User88f12472016-06-24 23:31:02 +02002309
2310 // Ausgabenachricht bauen und an den Spieler senden.
2311 if (flags & MSG_DONT_WRAP)
2312 msg = (msg_prefix ? msg_prefix : "") + msg;
2313 else
2314 {
2315 int bsflags = flags & MSG_ALL_BS_FLAGS;
2316 if (QueryProp(P_MESSAGE_PREPEND))
2317 bsflags |= BS_PREPEND_INDENT;
2318 msg = break_string(msg, 78, msg_prefix, bsflags);
2319 }
2320 efun::tell_object(ME, msg);
2321
2322 return MSG_DELIVERED;
2323}
Zesstra7459f252022-02-23 22:47:26 +01002324
2325// called from base.c in Reconnect()
2326protected void reconnect() {
2327 // Cache fuer den report zuruecksetzen, der koennte veraltet sein (insb.
2328 // falls in der letzten Session GMCP benutzt wurde und jetzt nicht).
2329 report_cache = 0;
2330}
2331
2332protected void updates_after_restore(int newflag) {
2333 // Colourmap aktualisieren nach Restore
2334 colourmap = build_colourmap(QueryProp(P_TTY));
2335
2336 // Altes Ignoriere loeschen...
2337 mixed ign = Query(P_IGNORE,F_VALUE);
2338 if (!mappingp(ign))
2339 {
2340 if (pointerp(ign))
2341 ReceiveNotify(break_string(
2342 "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
2343 "weil es eine Aktualisierung der Ignorierefunktion gab, "
2344 "bei der eine Konversion der Daten leider nicht "
2345 "moeglich war.",78), 0);
2346
2347 Set(P_IGNORE, ([]), F_VALUE);
2348 }
2349 // ggf. Comm-Vault abrufen oder neu erstellen.
2350 setup_comm_vault();
2351 // Wenn es eins gibt, den Inhalt zu unserem internen Koboldpuffer
2352 // hinzufuegen, von wo es spaeter angezeigt wird.
2353 if (commvault)
2354 process_comm_vault(commvault);
Bugfix895dc452025-06-30 17:35:45 +02002355
Zesstra47bff292025-07-08 21:40:05 +02002356 // P_ALERT korrigieren. Das historische Originalverhalten war, bei P_ALERT
2357 // == 0 alle Klingeltoene abzuspielen. Default fuer heute soll die gezielte
2358 // Kommunikation an den Spieler einschliessen.
Bugfix895dc452025-06-30 17:35:45 +02002359 if(!QueryProp(P_ALERT))
2360 {
Zesstra47bff292025-07-08 21:40:05 +02002361 SetProp(P_ALERT, MB_TELL|MB_MENTION|MB_MISC);
Bugfix895dc452025-06-30 17:35:45 +02002362 }
Zesstra7459f252022-02-23 22:47:26 +01002363}
2364