blob: 03c4c9e5d92952759279e7438eaac68f265e5d58 [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
MG Mud User88f12472016-06-24 23:31:02 +020084public int RemoveIgnore(string ign);
85public int AddIgnore(string ign);
86
87public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
88 string msg_prefix, object origin);
Zesstra7459f252022-02-23 22:47:26 +010089private int check_ignores(string msg, int msg_type, string msg_action,
90 string msg_prefix, object|string origin);
91private varargs void add_struct_tell_history(struct kobold_msg_s msg,
92 int sent, int recv, int flags );
MG Mud User88f12472016-06-24 23:31:02 +020093
94// erzeugt sortierte Liste an Kommunikationspartnern
95private string *sorted_commpartners(int reversed);
96
Zesstra7459f252022-02-23 22:47:26 +010097protected void create()
MG Mud User88f12472016-06-24 23:31:02 +020098{
99 ::create();
Bugfix3afcb792022-01-21 22:32:42 +0100100 Set(P_ALERT, SAVE, F_MODE_AS);
Zesstra47bff292025-07-08 21:40:05 +0200101 SetProp(P_ALERT, MB_TELL|MB_MENTION|MB_MISC);
MG Mud User88f12472016-06-24 23:31:02 +0200102 Set(P_EARMUFFS, 0);
103 Set(P_EARMUFFS, SAVE, F_MODE);
104 Set(P_EARMUFFS, SECURED, F_MODE);
MG Mud User88f12472016-06-24 23:31:02 +0200105 Set(P_IGNORE, ([]), F_VALUE);
106 Set(P_IGNORE, SAVE, F_MODE);
107 Set(P_BUFFER, SAVE, F_MODE);
Zesstra7459f252022-02-23 22:47:26 +0100108 SetProp(P_BUFFER, KOBOLD_OFFLINE);
MG Mud User88f12472016-06-24 23:31:02 +0200109 Set(P_MESSAGE_PREPEND, SAVE, F_MODE_AS);
110 Set(P_MESSAGE_BEEP, SAVE, F_MODE_AS);
111}
112
113void create_super()
114{
115 set_next_reset(-1);
116}
117
Zesstra3a261e52022-02-10 14:00:31 +0100118private mapping build_colourmap(string ttype)
119{
120 mapping res = ([0:""]);
121 switch(ttype)
122 {
123 case "dumb":
124 return res;
125 case "ansi":
126 res = ([ 0:ANSI_NORMAL, "normal": ANSI_NORMAL,
127 "bold": ANSI_BOLD, "underlined": ANSI_UNDERL,
128 "blink": ANSI_BLINK, "invers": ANSI_INVERS,
129 "black": ANSI_BLACK, "red": ANSI_RED,
130 "green": ANSI_GREEN, "yellow": ANSI_YELLOW,
131 "blue": ANSI_BLUE, "purple": ANSI_PURPLE,
132 "cyan": ANSI_CYAN, "white": ANSI_WHITE,
133 "bg_black": ANSI_BG_BLACK, "bg_red": ANSI_BG_RED,
134 "bg_green": ANSI_BG_GREEN, "bg_yellow": ANSI_BG_YELLOW,
135 "bg_blue": ANSI_BG_BLUE, "bg_purple": ANSI_BG_PURPLE,
136 "bg_cyan": ANSI_BG_CYAN, "bg_white": ANSI_BG_WHITE,
137 "mention": ANSI_BOLD+ANSI_BG_BLUE,
138 ]);
139 break;
140 case "vt100":
141 res += ([0:ANSI_NORMAL, "normal": ANSI_NORMAL,
142 "bold": ANSI_BOLD, "underlined": ANSI_UNDERL,
143 "blink": ANSI_BLINK, "invers": ANSI_INVERS,
144 "mention": ANSI_BOLD,
145 ]);
146 break;
147 default:
148 assert(1, "Ungueltiger Terminaltyp in build_colourmap");
149 }
150 return res;
151}
152
153protected void set_colourmap(string ttype="ansi")
154{
155 colourmap = build_colourmap(ttype);
156}
157
Zesstra7459f252022-02-23 22:47:26 +0100158private void setup_comm_vault()
159{
160 if (!commvault && (QueryProp(P_BUFFER) & KOBOLD_OFFLINE))
MG Mud User88f12472016-06-24 23:31:02 +0200161 {
Zesstra7459f252022-02-23 22:47:26 +0100162 // Schauen, ob ein Vault im KOBOLD existiert.
163 commvault = KOBOLD->RetrieveVault();
164 // Wenn nicht, aber eins gewuenscht ist, wird eins erstellt und in KOBOLD
165 // hinterlegt.
166 if (!commvault)
167 {
168 commvault = new_lwobject("/std/player/comm_vault");
169 KOBOLD->DepositVault(commvault);
170 }
MG Mud User88f12472016-06-24 23:31:02 +0200171 }
172}
173
Zesstra2504b2d2020-05-22 12:30:17 +0200174static int set_report(string str)
175{
MG Mud User88f12472016-06-24 23:31:02 +0200176 int canflags = QueryProp(P_CAN_FLAGS);
MG Mud User88f12472016-06-24 23:31:02 +0200177 if(!str)
178 {
Zesstracf8f2952020-05-22 12:07:52 +0200179 if (stat_reports)
180 {
181 string *res=({});
182 if (stat_reports & DO_REPORT_HP)
183 res+=({"Lebenspunkte"});
184 if (stat_reports & DO_REPORT_SP)
185 res+=({"Konzentrationspunkte"});
186 if (stat_reports & DO_REPORT_POISON)
187 res+=({"Vergiftungen"});
188 if (stat_reports & DO_REPORT_WIMPY)
189 res+=({"Vorsicht"});
MG Mud User88f12472016-06-24 23:31:02 +0200190
Zesstracf8f2952020-05-22 12:07:52 +0200191 tell_object(ME,break_string(
MG Mud User88f12472016-06-24 23:31:02 +0200192 "Dir werden jetzt Veraenderungen Deiner "
193 +CountUp(res) + " berichtet.",78));
Zesstra86ec63b2020-05-22 12:09:56 +0200194 if (GMCP_Status("MG.char") || GMCP_Status("char")
195 || GMCP_Status("Char"))
196 {
197 tell_object(ME,break_string(
198 "Achtung: Dein Client laesst sich den Report per GMCP "
Zesstra2504b2d2020-05-22 12:30:17 +0200199 "(s. 'hilfe GMCP') uebermitteln. Daher wird er Dir nicht "
Zesstra86ec63b2020-05-22 12:09:56 +0200200 "in der Textausgabe des Spiels angezeigt! Moechtest Du "
201 "dies nicht, schalte bitte in Deinem Client GMCP-Module mit "
202 "Namen wie 'MG.char', 'char', 'Char' oder aehnliche aus."));
203 }
MG Mud User88f12472016-06-24 23:31:02 +0200204 }
205 else
206 tell_object(ME,
207 "Alle Statusreports sind ausgeschaltet.\n");
208
209 return 1;
210 }
Zesstra2504b2d2020-05-22 12:30:17 +0200211 else if (str == "aus")
212 {
213 if (stat_reports & DO_REPORT_HP || stat_reports & DO_REPORT_WIMPY)
214 {
MG Mud User88f12472016-06-24 23:31:02 +0200215 string s="";
Zesstra2504b2d2020-05-22 12:30:17 +0200216 if (stat_reports & DO_REPORT_HP)
217 {
MG Mud User88f12472016-06-24 23:31:02 +0200218 str="ebenfalls ";
219 tell_object(ME, "Der Report wurde ausgeschaltet.\n");
220 }
Zesstra2504b2d2020-05-22 12:30:17 +0200221 if ( stat_reports & DO_REPORT_WIMPY )
222 {
MG Mud User88f12472016-06-24 23:31:02 +0200223 tell_object(ME, "Der Vorsicht-Report wurde "+s+
224 "ausgeschaltet.\n");
225 }
226 stat_reports=0;
227 }
Zesstra2504b2d2020-05-22 12:30:17 +0200228 else
229 {
MG Mud User88f12472016-06-24 23:31:02 +0200230 tell_object(ME, "Der Report ist bereits ausgeschaltet.\n");
231 }
232 return 1;
233 }
Zesstra2504b2d2020-05-22 12:30:17 +0200234 else if (str == "ein")
235 {
236 if ( stat_reports & DO_REPORT_HP )
237 {
MG Mud User88f12472016-06-24 23:31:02 +0200238 tell_object(ME, "Der Report ist bereits eingeschaltet.\n");
239 return 1;
240 }
241 tell_object(ME, "Der Report wurde eingeschaltet.\n");
242 stat_reports |= DO_REPORT_HP;
Zesstra2504b2d2020-05-22 12:30:17 +0200243 if (!(canflags & CAN_REPORT_SP))
244 {
245 if (QueryQuest("Hilf den Gnarfen")==1)
246 {
MG Mud User88f12472016-06-24 23:31:02 +0200247 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_SP);
248 stat_reports |= DO_REPORT_SP;
249 }
Zesstra2504b2d2020-05-22 12:30:17 +0200250 else
251 {
MG Mud User88f12472016-06-24 23:31:02 +0200252 tell_object(ME, break_string(
253 "Fuer den Statusreport Deiner Konzentration musst Du jedoch "
254 "zunaechst die Quest \"Hilf den Gnarfen\" bestehen.",78));
255 }
256 }
Zesstra2504b2d2020-05-22 12:30:17 +0200257 else
258 {
MG Mud User88f12472016-06-24 23:31:02 +0200259 stat_reports |= DO_REPORT_SP;
260 }
Zesstra2504b2d2020-05-22 12:30:17 +0200261 if (!(canflags & CAN_REPORT_POISON))
262 {
263 if (QueryQuest("Katzenjammer")==1)
264 {
MG Mud User88f12472016-06-24 23:31:02 +0200265 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_POISON);
266 stat_reports |= DO_REPORT_POISON;
267 }
Zesstra2504b2d2020-05-22 12:30:17 +0200268 else
269 {
MG Mud User88f12472016-06-24 23:31:02 +0200270 tell_object(ME, break_string(
271 "Fuer den Statusreport Deiner Vergiftung musst Du jedoch "
272 "zunaechst die Quest \"Katzenjammer\" bestehen.",78));
273 }
274 }
Zesstra2504b2d2020-05-22 12:30:17 +0200275 else
276 {
MG Mud User88f12472016-06-24 23:31:02 +0200277 stat_reports |= DO_REPORT_POISON;
278 }
279 // Cache loeschen, damit beim naechsten Report-Event alle Daten neu
280 // eingetragen werden muessen. Muss beim Einschalten des Reports
281 // passieren, weil auch in der inaktiven Zeit weiterhin Aenderungen in
282 // status_report() eingehen, so dass der Cache zwar erst einmal leer ist,
283 // aber beim Wiedereinschalten nicht mehr ungueltig waere und somit
284 // veraltete Daten an den Spieler ausgegeben werden. Im unguenstigsten
285 // Fall wuerde das sogar dazu fuehren, dass die veralteten Daten lange
286 // Zeit nicht aktualisiert werden, wenn z.B. P_HP == P_MAX_HP, so dass
287 // kein P_HP-Event mehr eingeht.
288 report_cache=0;
Zesstra2504b2d2020-05-22 12:30:17 +0200289 // Fall-through fuer Statusausgabe
MG Mud User88f12472016-06-24 23:31:02 +0200290 }
Zesstra2504b2d2020-05-22 12:30:17 +0200291 else if (str == "vorsicht")
292 {
293 if (!(canflags & CAN_REPORT_WIMPY))
294 {
295 if (QueryQuest("Schrat kann nicht einschlafen")==1)
296 {
MG Mud User88f12472016-06-24 23:31:02 +0200297 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_WIMPY);
298 tell_object(ME, "Der Vorsicht-Report wurde eingeschaltet.\n");
299 stat_reports |= DO_REPORT_WIMPY;
300 }
Zesstra2504b2d2020-05-22 12:30:17 +0200301 else
302 {
MG Mud User88f12472016-06-24 23:31:02 +0200303 tell_object(ME, break_string(
304 "Fuer den Statusreport Deiner Vorsicht musst Du "
305 "zunaechst die Quest \"Schrat kann nicht einschlafen\" "
306 "bestehen.",78));
307 }
308 }
309 else
310 {
311 stat_reports |= DO_REPORT_WIMPY;
312 }
313 // fuer Seher auch Bericht der Fluchtrichtung einschalten.
314 if ((stat_reports & DO_REPORT_WIMPY)
315 && !(stat_reports & DO_REPORT_WIMPY_DIR)
316 && ((canflags & CAN_REPORT_WIMPY) || IS_SEER(ME)))
317 {
Zesstra2504b2d2020-05-22 12:30:17 +0200318 stat_reports |= DO_REPORT_WIMPY_DIR;
MG Mud User88f12472016-06-24 23:31:02 +0200319 }
Zesstra2504b2d2020-05-22 12:30:17 +0200320 // Fall-through fuer Statusausgabe
MG Mud User88f12472016-06-24 23:31:02 +0200321 }
322 // sendet einmalig genau jetzt den konfigurierten report. Kann zum testen
323 // (von Triggern) oder beim Login benutzt werden, wenn man einen initialen
324 // Datenbestand erhalten will.
325 else if (str=="senden")
326 {
327 // Es wird Ausgabe von LP und Vorsicht getriggert, das sendet beide
328 // Zeilen.
329 status_report(DO_REPORT_HP, QueryProp(P_HP));
330 status_report(DO_REPORT_WIMPY, QueryProp(P_WIMPY));
331 return 1;
332 }
333 else
334 return 0;
335 // nur aktuellen Zustand berichten
336 set_report(0);
337 return 1;
338}
339
340private string get_poison_desc(int p) {
341 string ret;
342 if ( intp(p) ) {
343 switch(p) {
344 case 0: ret="keins"; break;
345 case 1..3: ret="leicht"; break;
346 case 4..8: ret="gefaehrlich"; break;
347 default: ret="sehr ernst"; break;
348 }
349 return ret;
350 }
351 else return "(nicht verfuegbar)";
352}
353
354// sprintf()-Formatstrings fuer die Reportausgabe.
355#define REPORTLINE "LP: %3d, KP: %3s, Gift: %s.\n"
356#define REPORTLINE_WIMPY "Vorsicht: %d, Fluchtrichtung: %s.\n"
357// Defines zur Adressierung der Cache-Eintraege
358#define REP_HP 0
359#define REP_SP 1
360#define REP_POISON 2
361
362protected void status_report(int type, mixed val) {
363 // Wenn der Spieler GMCP hat und das sich um die Information kuemmert,
364 // erfolgt keine textuelle Ausgabe mehr. Daher return, wenn GMCP_Char()
365 // erfolg vermeldet hat.
366 int flags = QueryProp(P_CAN_FLAGS);
367 switch (type) {
368 case DO_REPORT_HP:
369 if (GMCP_Char( ([ P_HP: val ]) ) ) return;
370 break;
371 case DO_REPORT_SP:
372 if (!(flags & CAN_REPORT_SP)) return;
373 if (GMCP_Char( ([ P_SP: val ]) ) ) return;
374 break;
375 case DO_REPORT_POISON:
376 if (!(flags & CAN_REPORT_POISON)) return;
377 if (GMCP_Char( ([ P_POISON: val ]) ) ) return;
378 break;
379 case DO_REPORT_WIMPY:
380 if (!(flags & CAN_REPORT_WIMPY)) return;
381 if (GMCP_Char( ([ P_WIMPY: val ]) ) ) return;
382 break;
383 case DO_REPORT_WIMPY_DIR:
384 if (!(flags & CAN_REPORT_WIMPY_DIR)) return;
385 if (GMCP_Char( ([ P_WIMPY_DIRECTION: val ]) ) ) return;
386 break;
387 }
388
389 // konventionelle textuelle Ausgabe des Reports ab hier.
390 if (!(type & stat_reports))
391 return;
392
393 if ( !report_cache ) {
394 report_cache = ({
395 QueryProp(P_HP),
396 (stat_reports&DO_REPORT_SP) ? to_string(QueryProp(P_SP)) : "###",
397 (stat_reports&DO_REPORT_POISON) ?
398 get_poison_desc(QueryProp(P_POISON)) : "(nicht verfuegbar)"
399 });
400 }
401
402 switch(type) {
403 // LP berichten: Cache aktualisieren und Meldung ausgeben.
404 case DO_REPORT_HP:
405 report_cache[REP_HP]=val;
406 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
407 report_cache[REP_SP], report_cache[REP_POISON]));
408 break;
409 // KP berichten: Wenn der Spieler den Report freigeschaltet hat,
410 // wird bei Aenderungen gemeldet. Wenn nicht, aendert sich nur der
411 // Cache-Eintrag. So wird verhindert, dass ein Spieler ueber KP-
412 // Veraenderungen auch dann informiert wuerde, wenn er den KP-Report
413 // gar nicht benutzen koennte.
414 case DO_REPORT_SP:
415 report_cache[REP_SP]=to_string(val);
416 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
417 report_cache[REP_SP], report_cache[REP_POISON]));
418 break;
419 // Giftstatus berichten: Wenn der Giftreport freigeschaltet ist,
420 // Cache aktualisieren und berichten. Wenn nicht, aendert sich nur
421 // der Cache-Eintrag. Erlaeuterung hierzu s.o. beim KP-Report.
422 case DO_REPORT_POISON:
423 report_cache[REP_POISON] = get_poison_desc(val);
424 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
425 report_cache[REP_SP], report_cache[REP_POISON]));
426 break;
427 // Vorsicht-Report: kann ohne weitere Abfragen ausgegeben werden, da
428 // alle noetigen Checks schon zu Beginn dieser Funktion erledigt wurden.
429 // Lediglich der Inhalt der Meldung muss abhaengig vom Seherstatus
430 // konfiguriert werden.
431 case DO_REPORT_WIMPY:
432 string res;
433 if (IS_SEER(ME)) {
434 // QueryProp() aus Kostengruenden im if(), damit die Aufruf-
435 // Haeufigkeit zumindest ein wenig reduziert wird.
436 string dir = QueryProp(P_WIMPY_DIRECTION)||"keine";
437 res = sprintf(REPORTLINE_WIMPY, val, dir);
438 }
439 else
440 res = sprintf(REPORTLINE_WIMPY, val, "(nicht verfuegbar)");
441 tell_object(ME, res);
442 break;
443 // Fluchtrichtungs-Report: wird nur bei Sehern ausgegeben, damit
444 // nicht auch Spieler eine VS-/FR-Meldung bekommen, wenn z.B. eine
445 // externe Manipulation der Fluchtrichtung stattfindet, sie aber den
446 // Report mangels Seherstatus gar nicht freigeschaltet haben.
447 case DO_REPORT_WIMPY_DIR:
448 if (IS_SEER(ME)) {
449 if (!val) val = "keine";
450 tell_object(ME,sprintf(REPORTLINE_WIMPY, QueryProp(P_WIMPY), val));
451 }
452 break;
453 }
454}
455
456#undef REPORTLINE
457#undef REPORTLINE_WIMPY
458#undef REP_HP
459#undef REP_SP
460#undef REP_POISON
461
462private string permutate(string msg)
463{
464 // Kontrollzeichen rausfiltern. *seufz*
465 msg = regreplace(msg,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
466 object ob=QueryProp(P_PERM_STRING);
467 if (!objectp(ob))
468 return msg;
469
Zesstra04f613c2019-11-27 23:32:54 +0100470 return ({string})ob->permutate_string(msg)||"";
MG Mud User88f12472016-06-24 23:31:02 +0200471}
472
473// neue nachricht an den Kobold anhaengen
474// Rueckgabewerte: MSG_BUFFER_FULL oder MSG_BUFFERED
475private int add_to_kobold(string msg, int msg_type, string msg_action,
476 string msg_prefix, object origin)
477{
478 // Nachricht soll im Kobold gespeichert werden.
479 // Kobold speichert Rohdaten und gibt spaeter das ganze auch wieder via
480 // ReceiveMsg() aus - dabei wird MSG_DONT_BUFFER | MSG_DONT_STORE gesetz,
481 // damit keine erneute Speicher in Kobold oder Komm-History erfolgt.
482
483 // wenn der Puffer zu klein ist, Groesse verdoppeln, wenn noch unterhalb
484 // des Limits.
485 if (kobold->index >= sizeof(kobold->buf)-1) {
486 if (sizeof(kobold->buf) < MAX_KOBOLD_LIMIT)
487 kobold->buf += allocate(sizeof(kobold->buf));
488 else
489 return MSG_BUFFER_FULL;
490 }
491 kobold->index = kobold->index +1;
492 // neue Nachricht an den Puffer anhaengen.
493 string sendername = query_once_interactive(origin) ?
494 origin->query_real_name() :
495 origin->name(WER) || "<Unbekannt>";
Zesstraa5fda4a2022-01-06 17:31:44 +0100496 kobold->buf[kobold->index] = (<kobold_msg_s> msg: msg,
MG Mud User88f12472016-06-24 23:31:02 +0200497 type : msg_type, action : msg_action, prefix : msg_prefix,
Zesstra2ab528e2025-07-26 19:07:28 +0200498 sendername : sendername, timestamp: time());
MG Mud User88f12472016-06-24 23:31:02 +0200499 return MSG_BUFFERED;
500}
501
Zesstra7459f252022-02-23 22:47:26 +0100502// speichert den Inhalt vom commvault im Kobold und der TM-History
503private void process_comm_vault(lwobject "/std/player/comm_vault" vault)
504{
505 struct kobold_msg_s *buffer = vault.Retrieve();
506 if (!sizeof(buffer))
507 return;
508 foreach(struct kobold_msg_s msg: buffer)
509 {
510 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
511 int res = check_ignores(msg.msg, msg.type, msg.action, msg.prefix,
512 msg.sendername);
513 if (res) {
514 // Nachricht wegwerfen. Aber ggf. den Absender informieren, wenn der
515 // online ist und wir nicht Invis
516 object pl = find_player(msg.sendername);
517 if (pl &&
518 (!QueryProp(P_INVIS) || IS_LEARNER(pl)) )
519 pl->ReceiveNotify(sprintf("Deine Nachricht an %s wurde "
520 "ignoriert.",capitalize(getuid(this_object()))), MA_TELL);
521 continue;
522 }
523
524 // wenn der Puffer zu klein ist, Groesse verdoppeln.
525 // Keine Pruefung hier, weil das vault schon die Groesse beschraenkt und
526 // der Inhalt auf jeden Fall passen soll.
527 if (kobold->index >= sizeof(kobold->buf)-1)
528 kobold->buf += allocate(sizeof(kobold->buf));
529 kobold->index += 1;
530 kobold->buf[kobold->index] = msg;
531
532 // TM-History
533 add_struct_tell_history(msg, 0, 1, MSGFLAG_TELL);
534 }
535 vault.Empty();
536}
537
MG Mud User88f12472016-06-24 23:31:02 +0200538private void _flush_cache(int verbose) {
539 // nur mit genug Evalticks ausgeben.
Zesstra7459f252022-02-23 22:47:26 +0100540 if (get_eval_cost() < 500000) return;
MG Mud User88f12472016-06-24 23:31:02 +0200541 if (kobold->index >= 0)
542 {
543 ReceiveMsg("Ein kleiner Kobold teilt Dir folgendes mit:",
544 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
545 0, 0, this_object());
546 int prepend = QueryProp(P_MESSAGE_PREPEND);
547 foreach(int i: 0 .. kobold->index) // '0 ..' ist wichtig!
548 {
Zesstraa5fda4a2022-01-06 17:31:44 +0100549 struct kobold_msg_s msg = kobold->buf[i];
MG Mud User88f12472016-06-24 23:31:02 +0200550 // dies ist dient der Fehlerabsicherung, falls es nen Fehler (z.B. TLE)
551 // in der Schleife unten gab: dann ist index nicht auf -1 gesetzt
552 // worden, aber einige Nachrichten sind schon geloescht.
553 if (!structp(msg)) continue;
Zesstra7459f252022-02-23 22:47:26 +0100554 // Im folgenden nicht den string in der struct aendern (die wird ggf.
555 // auch noch von der TM-History gebraucht).
556 string msgstr = msg.msg;
557 // Wenn Nachricht schon laenger her ist, Uhrzeit anhaengen, aber ggf.
Zesstradeee2052025-07-26 19:02:31 +0200558 // muss ein \n abgeschnitten und wieder angehaengt werden.
Zesstra7459f252022-02-23 22:47:26 +0100559 if (msg.timestamp < time() - 3600)
560 {
561 if (msgstr[<1] == '\n')
562 msgstr = msgstr[0..<2]
Zesstradeee2052025-07-26 19:02:31 +0200563 + " [" + strftime("%d.%m.%y %T", msg.timestamp) + "]\n";
Zesstra7459f252022-02-23 22:47:26 +0100564 else
565 msgstr = msgstr
566 + " [" + strftime("%d.%m.%y %T", msg.timestamp) + "]";
567 }
MG Mud User88f12472016-06-24 23:31:02 +0200568 // Ausgabe via efun::tell_object(), weil die Arbeit von ReceiveMsg()
Zesstra3a261e52022-02-10 14:00:31 +0100569 // schon getan wurde. Allerdings muessen wir uns noch um den Umbruch
570 // und Farben kuemmern.
Zesstra7459f252022-02-23 22:47:26 +0100571 msgstr = terminal_colour(msgstr, colourmap);
MG Mud User88f12472016-06-24 23:31:02 +0200572 if ((msg->type) & MSG_DONT_WRAP)
Zesstra7459f252022-02-23 22:47:26 +0100573 msgstr = (msg->prefix ? msg->prefix : "") + msgstr;
MG Mud User88f12472016-06-24 23:31:02 +0200574 else
575 {
576 int bsflags = msg->type & MSG_ALL_BS_FLAGS;
577 if (prepend)
578 bsflags |= BS_PREPEND_INDENT;
Zesstra7459f252022-02-23 22:47:26 +0100579 msgstr = break_string(msgstr, 78, msg->prefix, bsflags);
MG Mud User88f12472016-06-24 23:31:02 +0200580 }
Zesstra7459f252022-02-23 22:47:26 +0100581 efun::tell_object(this_object(), msgstr);
MG Mud User88f12472016-06-24 23:31:02 +0200582 kobold->buf[i]=0;
583 }
584 kobold->index=-1;
585 }
586 else if (verbose)
587 {
588 ReceiveMsg("Der kleine Kobold hat leider nichts Neues fuer Dich.",
589 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
590 0, 0, this_object());
591 }
592}
593
594varargs int cmd_kobold(string arg)
595{
Zesstra7459f252022-02-23 22:47:26 +0100596 if (!sizeof(arg))
597 {
598 _flush_cache(1);
599 return 1;
600 }
MG Mud User88f12472016-06-24 23:31:02 +0200601 switch(arg)
602 {
603 case "ein":
Zesstra7459f252022-02-23 22:47:26 +0100604 SetProp(P_BUFFER, KOBOLD_ONLINE|KOBOLD_OFFLINE);
605 ReceiveNotify("Der Kobold merkt sich jetzt alles!");
606 break;
607 case "online":
608 SetProp(P_BUFFER, KOBOLD_ONLINE);
609 ReceiveNotify("Der Kobold merkt sich jetzt alles, "
610 "wenn Du online bist!");
611 break;
612 case "offline":
613 SetProp(P_BUFFER, KOBOLD_OFFLINE);
614 ReceiveNotify("Der Kobold merkt sich jetzt alles, "
615 "wenn Du offline bist!");
616 break;
MG Mud User88f12472016-06-24 23:31:02 +0200617 case "aus":
618 SetProp(P_BUFFER, 0);
Zesstra7459f252022-02-23 22:47:26 +0100619 ReceiveNotify("Der Kobold wird Dich nicht stoeren!");
620 break;
621 default:
622 ReceiveNotify("Der Kobold sagt: Was soll ich mir denn merken? "
623 "('ein', 'aus', 'offline' oder 'online')");
624 return 1;
MG Mud User88f12472016-06-24 23:31:02 +0200625 }
Zesstra7459f252022-02-23 22:47:26 +0100626 if (QueryProp(P_BUFFER) & KOBOLD_OFFLINE)
627 setup_comm_vault();
628 else
629 {
630 // Comm-Vault entfernen. Aber zur Sicherheit nochmal abrufen und
631 // verarbeiten (sollte aber eigentlich ueberfluessig sein)
632 commvault = KOBOLD->ForgetVault();
633 if (commvault) {
634 process_comm_vault(commvault);
635 commvault = 0;
636 }
637 }
MG Mud User88f12472016-06-24 23:31:02 +0200638 return 1;
639}
640
641public int TestIgnoreSimple(string *arg)
Zesstrade642d22019-11-23 17:22:34 +0100642{ mapping ignore;
MG Mud User88f12472016-06-24 23:31:02 +0200643
644 if (!pointerp(arg) || !mappingp(ignore=Query(P_IGNORE,F_VALUE)))
645 return 0;
646 foreach(string s: arg)
647 {
648 if (member(ignore,s))
649 return 1;
650 }
651 return 0;
652}
653
654//TODO: deprecated - entfernen, wenn Message() entfernt wird.
655private int check_ignore(mixed ignore, string verb, string name)
656{
657 if (ignore == verb)
658 return 1;
659 ignore = explode(ignore, ".");
660 return ((sizeof(ignore) > 1) &&
661 (name == ignore[0] && member(ignore[1..], verb) != -1));
662}
663
Zesstra3a261e52022-02-10 14:00:31 +0100664// Die Nachricht kommt mit einem Alert oder Mention. comm_beep() prueft, ob
665// ein Piepston ausgegeben werden soll (langfristig andere Benachrichtigungen
666// geplant!).
667// Ueblicherweise werden nur alle <P_MESSAGE_BEEP> Sekunden Toene
668// uebermittelt.
669private void comm_beep(string msg_action, int mention=0)
Bugfix60f5bc62022-01-21 11:09:56 +0100670{
Zesstra1cc6cf32022-02-09 11:49:31 +0100671 // Wenn Alerts komplett abgeschaltet sind, gibts nix.
672 int alert_config = ({int})QueryProp(P_ALERT);
673 if (alert_config & AL_NO_SOUND)
674 return; // kein ton
675 // und maximal alle P_MESSAGE_BEEP Sekunden aufmerksam machen; bei 0s ist
676 // jedes Mal erlaubt.
Zesstra04f613c2019-11-27 23:32:54 +0100677 int beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
Zesstra1cc6cf32022-02-09 11:49:31 +0100678 if ((time()-last_beep_time) < beep_interval)
679 return;
680 int required;
681 switch(msg_action)
Bugfix60f5bc62022-01-21 11:09:56 +0100682 {
Zesstra1cc6cf32022-02-09 11:49:31 +0100683 case MA_TELL:
684 required |= MB_TELL;
685 break;
686 case MA_SAY:
687 required |= MB_SAY;
688 break;
689 case MA_CHANNEL:
690 required |= MB_CHANNEL;
691 break;
692 case MA_SHOUT:
693 required |= MB_SHOUT;
694 break;
695 default:
696 // Alle anderen Aktionen, die keine gesonderten Aktionsfilter haben
697 required |= MB_MISC;
698 break;
Bugfix60f5bc62022-01-21 11:09:56 +0100699 }
Zesstra3a261e52022-02-10 14:00:31 +0100700 if (mention)
701 required |= MB_MENTION;
702
Zesstra1cc6cf32022-02-09 11:49:31 +0100703 // wenn in alert min. ein notwendiges Flag drin ist darf gepiepst werden
704 if (required & alert_config)
705 {
706 last_beep_time = time();
707 binary_message(b"\a", 1);
708 }
MG Mud User88f12472016-06-24 23:31:02 +0200709}
710
Zesstra7459f252022-02-23 22:47:26 +0100711private varargs void add_struct_tell_history(struct kobold_msg_s msg,
712 int sent, int recv, int flags )
MG Mud User88f12472016-06-24 23:31:02 +0200713{
714 /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
715 Als Wert ist eine Strukur vom Typ chat_s eingetragen.
Zesstra7459f252022-02-23 22:47:26 +0100716 Strukturen chat_s und kobold_msg_s sind in /std/player/comm_structs.c
MG Mud User88f12472016-06-24 23:31:02 +0200717 definiert.
718 TODO fuer spaeter, gerade keine Zeit fuer:
719 Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
720 jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
721 weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
722 Element ist aeltestes Gespraech).
723 */
724
Zesstra996bafe2022-02-14 22:42:39 +0100725 // Gespraechspartner fuer erwidere auch ohne tmhist speichern.
Zesstraa31cd5c2016-12-17 20:11:07 +0100726 if (flags & (MSGFLAG_TELL|MSGFLAG_RTELL))
Zesstra7459f252022-02-23 22:47:26 +0100727 last_comm_partner = msg.sendername;
MG Mud User88f12472016-06-24 23:31:02 +0200728
729 // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
730 // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
731 // Anfang.
732 if (sent) {
733 if (!sizeof(commreceivers))
Zesstra7459f252022-02-23 22:47:26 +0100734 commreceivers = ({msg.sendername});
735 else if (commreceivers[0] != msg.sendername) {
MG Mud User88f12472016-06-24 23:31:02 +0200736 // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
737 // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
738 // entfernen.
739 // TODO: Effizienter gestalten.
Zesstra7459f252022-02-23 22:47:26 +0100740 commreceivers = ({msg.sendername}) + (commreceivers-({msg.sendername}));
MG Mud User88f12472016-06-24 23:31:02 +0200741 // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
742 // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
743 // commreceivers drin sind.)
744 if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
745 commreceivers = commreceivers[0..MAX_SAVED_CHATS];
746 }
747 }
748
Zesstra996bafe2022-02-14 22:42:39 +0100749 // mit eingeschalteter History weitere Details speichern.
MG Mud User88f12472016-06-24 23:31:02 +0200750 if (!tell_history_enabled)
751 return;
752
Zesstra7459f252022-02-23 22:47:26 +0100753 if (msg.msg[<1] == '\n')
754 msg.msg = msg.msg[..<2];
MG Mud User88f12472016-06-24 23:31:02 +0200755
756 struct chat_s chat;
757 // Gespraechspartner unbekannt?
Zesstra7459f252022-02-23 22:47:26 +0100758 if (!member(tell_history, msg.sendername)) {
MG Mud User88f12472016-06-24 23:31:02 +0200759 // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
760 // dazu kommt.
761 if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
762 string deluid;
763 int zeit = __INT_MAX__;
764 foreach(string tuid, chat : tell_history) {
765 // aeltestes Gespraech suchen
766 if (zeit > chat->time_last_msg) {
767 deluid = tuid;
768 zeit = chat->time_last_msg;
769 }
770 }
771 // aeltestes Gespraech raus.
772 m_delete(tell_history, deluid);
773 if (member(commreceivers,deluid)>-1)
774 commreceivers-=({deluid});
775 }
776 // neues Gespraech anlegen
Zesstra7459f252022-02-23 22:47:26 +0100777 chat = (<chat_s> uid: msg.sendername, time_first_msg: msg.timestamp,
778 time_last_msg: msg.timestamp,
MG Mud User88f12472016-06-24 23:31:02 +0200779 sentcount: sent, recvcount: recv,
780 msgbuf: 0, ptr: 0 );
Zesstra7459f252022-02-23 22:47:26 +0100781 tell_history[msg.sendername] = chat;
MG Mud User88f12472016-06-24 23:31:02 +0200782 }
783 else {
784 // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
Zesstra7459f252022-02-23 22:47:26 +0100785 chat = tell_history[msg.sendername];
786 chat->time_last_msg = msg.timestamp;
MG Mud User88f12472016-06-24 23:31:02 +0200787 chat->sentcount += sent;
788 chat->recvcount += recv;
789 }
790
Zesstra996bafe2022-02-14 22:42:39 +0100791 // ggf. auch INhalte von Gespraechen speichern
MG Mud User88f12472016-06-24 23:31:02 +0200792 if (tell_history_enabled < TELLHIST_ENABLED)
793 return;
794
795 // ggf. Array fuer Messages anlegen
796 if (!pointerp(chat->msgbuf))
797 chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
798
Zesstra7459f252022-02-23 22:47:26 +0100799 // neue Struct ins Array schreiben
800 chat->msgbuf[chat->ptr] = msg;
MG Mud User88f12472016-06-24 23:31:02 +0200801 // Index auf naechste Messagestruktur ermitteln
802 chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
Zesstra7459f252022-02-23 22:47:26 +0100803}
804
805private varargs void add_to_tell_history( string uid, int sent, int recv,
806 string message, string indent, int flags )
807{
808 //TODO: Entfernen, wenn das nicht mehr passiert.
809 if (!stringp(uid))
810 {
811 ReceiveMsg(sprintf(
812 "\nadd_to_tell_history(): got bad uid argument %O."
813 "sent: %d, recv: %d, flags: %d, msg: %s",
814 uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);
815 }
816 // Message-Struktur anlegen
817 struct kobold_msg_s msg = (<kobold_msg_s> msg: message, prefix: indent,
818 sendername: uid, timestamp: time());
819 add_struct_tell_history(msg, sent, recv, flags);
MG Mud User88f12472016-06-24 23:31:02 +0200820}
821
Zesstra996bafe2022-02-14 22:42:39 +0100822protected void clear_tell_history(int force)
MG Mud User88f12472016-06-24 23:31:02 +0200823{
824 /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
825 sofern der Spieler nichts abweichendes eingestellt hat. */
826
Zesstra996bafe2022-02-14 22:42:39 +0100827 // bei manuellem Loeschen (force==1) immer loeschen, ansonsten nur, wenn die
828 // History nicht "long-life" ist.
829 if (tell_history_enabled < TELLHIST_LONGLIFE || force)
830 {
831 tell_history = ([]);
832 commreceivers = ({});
833 }
MG Mud User88f12472016-06-24 23:31:02 +0200834}
835
836protected void reset(void)
837{
838 /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
839 Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
840 wie bei einem "schlafe ein" die Mitteilungen geloescht. */
841
842 if (!interactive())
Zesstra996bafe2022-02-14 22:42:39 +0100843 clear_tell_history(0);
MG Mud User88f12472016-06-24 23:31:02 +0200844
845}
846
847// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
848// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
849// werden soll.
850private void _send(object ob, string msg, int msg_type,
851 string msg_action, string msg_prefix)
852{
853 int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
Zesstra8b5320e2022-02-18 21:10:26 +0100854
MG Mud User88f12472016-06-24 23:31:02 +0200855 switch(res) {
856 case MSG_DELIVERED:
857 break; // nix machen
858 case MSG_BUFFERED:
Zesstra62b4a862022-12-23 20:08:16 +0100859 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200860 "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
861 "genommen. Er wird sie spaeter weiterleiten!",
862 MT_NOTIFICATION, msg_action, 0, this_object());
863 break;
864 case MSG_IGNORED:
865 case MSG_VERB_IGN:
866 case MSG_MUD_IGN:
867 ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
868 MT_NOTIFICATION, msg_action, 0, this_object());
869 break;
870 case MSG_SENSE_BLOCK:
871 ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
872 MT_NOTIFICATION, msg_action, 0, this_object());
873 break;
874 case MSG_BUFFER_FULL:
Zesstra62b4a862022-12-23 20:08:16 +0100875 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200876 "Die Mitteilung ging verloren, denn der Kobold kann sich "
877 "nichts mehr merken!", MT_NOTIFICATION, msg_action,
878 0, this_object());
879 break;
880 default:
881 ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
882 "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
883 break;
884 }
885}
886
Zesstra8b5320e2022-02-18 21:10:26 +0100887// aehnlich wie _send(), aber gerufen, wenn die Empfaengerin nicht online ist,
888// aber ein Kobold-Vault deponiert hat und versucht werden soll, dort eine
889// Nachricht zu hinterlegen.
890private void _send_to_kobold(string pluid, string msg, int msg_type,
891 string msg_action, string msg_prefix)
892{
893 int res = KOBOLD->DepositMsg(pluid, msg, msg_type, msg_action,
894 msg_prefix, ME);
895 switch(res) {
896 case MSG_BUFFERED:
897 ReceiveMsg(sprintf("%s ist gerade nicht online, aber Deine Botschaft "
898 "wurde von einem fuer kleinen Kobold aufgeschrieben und er wird "
899 "versuchen, sie %s bei naechster Gelegenheit zu uebermitteln. "
900 "Der Kobold macht Dich darauf aufmerksam, dass er aber keine "
901 "Uebermittlung garantieren kann.",
902 capitalize(pluid), capitalize(pluid)),
903 MT_NOTIFICATION, msg_action, 0, this_object());
904 break;
905 case MSG_BUFFER_FULL:
906 ReceiveMsg(sprintf("%s ist gerade nicht online und leider ist auf "
907 "der Schriftrolle des kleinen Kobolds kein Platz mehr fuer "
908 "Deine Botschaft. Vielleicht schickst Du %s besser einen Brief.",
909 capitalize(pluid), capitalize(pluid)),
910 MT_NOTIFICATION, msg_action, 0, this_object());
911 break;
912 default:
913 ReceiveMsg(sprintf("%s ist gerade nicht online und leider konnte "
914 "sich der kleine Kobold Deine Botschaft nicht merken. "
915 "Vielleicht schickst Du %s besser einen Brief.",
916 capitalize(pluid),capitalize(pluid)),
917 MT_NOTIFICATION, msg_action, 0, this_object());
918 break;
919 }
920}
921
MG Mud User88f12472016-06-24 23:31:02 +0200922// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
923// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
924// die Nachricht ggf. in den Kommhistory erfasst wird.
925// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
926protected varargs int _recv(object ob, string message, int flag, string indent)
927{
928 write(break_string(message, 78, indent,
929 QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
930 if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
931 query_once_interactive(ob))
932 {
933 if (flag & MSGFLAG_WHISPER)
934 add_to_tell_history(getuid(ob), 1, 0,
935 "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
936 flag);
937 else
938 add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
939 }
940 return 1;
941}
942
943// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
944// Fehlerbehandlung) ist es jedoch auch mal ein String.
945varargs int Message(string msg, int flag, string indent,
946 string cname, mixed sender)
947{
948 object ti;
949 string verb, reply, *ignore, tin;
950 int em, te;
951 mixed deaf;
952
953 // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
954 // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
955 // damit 'ignoriere name.debug' funktioniert.
956 if( flag == MSGFLAG_CHANNEL ){
957 if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
958 || msg[1..9]=="Warnungen"))
959 {
960 // Missbrauch der Variable 'ignore' als Zwischenspeicher
961 ignore = regexplode( msg, ":| |\\]" );
962 verb = lower_case(ignore[0][1..]);
963 tin = lower_case(ignore[2]);
964 }
965 else
966 {
967 if(cname)
968 verb=lower_case(cname);
969 else
970 verb=query_verb();
971 if( ti = this_interactive() )
972 {
973 tin = getuid(this_interactive());
974 }
975 else
976 {
977 //falls doch kein Objekt...
978 if (objectp(sender))
979 tin=lower_case(sender->name(RAW)||"<Unbekannt>");
980 }
981 }
982 }
983 else {
984 if( ti = this_interactive() )
985 tin = getuid(this_interactive());
986 verb = query_verb();
987 }
988
989 te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
990
991 // fuer "erwidere"
992 if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
993 if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
994 if (flag & MSGFLAG_WHISPER)
995 add_to_tell_history(getuid(ti), 0, 1,
996 capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
997 (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
998 ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
999 " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
1000 else
1001 add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
1002 }
1003 }
1004 // Hoert der Spieler nicht?
1005 em = (ti &&
1006 (te || flag & MSGFLAG_SHOUT) &&
1007 (QueryProp(P_EARMUFFS) &&
1008 (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
1009 ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
1010
1011 // Werden der Sender oder das Verb ignoriert?
1012 if(!ti && tin && flag == MSGFLAG_CHANNEL)
1013 {
1014 if((member(ignore, tin) != -1))
1015 {
1016 return MESSAGE_IGNORE_YOU;
1017 }
1018 if(verb && sizeof(filter(ignore, #'check_ignore, verb, tin)) )
1019 {
1020 return MESSAGE_IGNORE_YOU;
1021 }
1022 }
1023 if (ti && (member(ignore, getuid(ti)) != -1)) {
1024 if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
1025 efun::tell_object(ti, capitalize(name())+
1026 " hoert gar nicht zu, was Du sagst.\n");
1027 return MESSAGE_IGNORE_YOU;
1028 }
1029 if(tin && verb &&
1030 sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
1031 {
1032 if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
1033 verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
1034 efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
1035 return MESSAGE_IGNORE_VERB;
1036 }
1037 if (flag & MSGFLAG_RTELL) {
1038 int at;
1039
1040 verb = lower_case(old_explode(msg, " ")[0][1..]);
1041 at = member(verb, '@');
1042 /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
1043 add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
1044
1045 if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
1046 return MESSAGE_IGNORE_YOU;
1047 else if (at > 0 && member(ignore, verb[at..]) >= 0)
1048 return MESSAGE_IGNORE_MUD;
1049 }
1050
1051 // Taubheit/Oropax
1052 te |= (flag & MSGFLAG_SAY);
1053
1054 if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
1055 deaf = QueryProp(P_DEAF);
1056 if (te)
1057 reply = stringp(deaf) ?
1058 capitalize(sprintf(deaf, name())) :
1059 capitalize(name())+" ist momentan leider taub.\n";
1060 }
1061 else if (em)
1062 reply = capitalize(name())+" hat Oropax in den Ohren.\n";
1063
1064 msg = break_string(msg, 78, indent,
1065 (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
1066
Zesstra7459f252022-02-23 22:47:26 +01001067 if((QueryProp(P_BUFFER) & KOBOLD_ONLINE) &&
MG Mud User88f12472016-06-24 23:31:02 +02001068 (deaf ||
1069 query_editing(this_object()) ||
1070 query_input_pending(this_object())))
1071 {
1072 deaf = MESSAGE_DEAF;
1073 if(flag & MSGFLAG_CACHE)
1074 {
1075 if(!stringp(reply))
1076 reply = name()+" moechte gerade nicht gestoert werden.\n";
1077
1078 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1079
1080 int res = add_to_kobold(msg, 0, 0, 0,
1081 objectp(sender) ? sender : ME);
1082 if(res == MSG_BUFFERED)
1083 {
1084
1085 reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
1086 "genommen.\nEr wird sie spaeter weiterleiten!";
1087 deaf = MESSAGE_CACHE;
1088 }
1089 else {
1090 reply += "Die Mitteilung ging verloren, denn "+
1091 "der Kobold kann sich nichts mehr merken!";
1092 deaf = MESSAGE_CACHE_FULL;
1093 }
1094 if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
1095 efun::tell_object(ti, reply+"\n");
1096 }
1097 return deaf;
1098 }
1099 else if((deaf || em) &&
1100 ( (flag & MSGFLAG_RTELL) ||
1101 (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
1102 if (te && ti)
1103 efun::tell_object(ti, reply);
1104 return MESSAGE_DEAF;
1105 }
1106
1107 _flush_cache(0);
1108 if(te && QueryProp(P_AWAY))
1109 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1110
Zesstra1cc6cf32022-02-09 11:49:31 +01001111 if(!objectp(sender) || sender != ME)
Bugfix60f5bc62022-01-21 11:09:56 +01001112 {
Zesstra1cc6cf32022-02-09 11:49:31 +01001113 if (flag & MSGFLAG_SAY)
1114 comm_beep(MA_SAY);
1115 else if (flag & MSGFLAG_TELL)
1116 comm_beep(MA_TELL);
1117 else if (flag & MSGFLAG_CHANNEL)
1118 comm_beep(MA_CHANNEL);
1119 else if (flag & MSGFLAG_SHOUT)
1120 comm_beep(MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001121 }
1122 efun::tell_object(ME, msg);
1123 return MESSAGE_OK;
1124}
1125
1126static int ignoriere(string str)
1127{
1128 str = _unparsed_args(1);
1129 mapping ignore=Query(P_IGNORE, F_VALUE);
1130
1131 if (!str)
1132 {
1133 string* ignarr = m_indices(ignore);
1134 if (!sizeof(ignarr))
1135 tell_object(ME, "Du ignorierst niemanden.\n");
1136 else
1137 ReceiveMsg("Du ignorierst:\n"
1138 + break_string(CountUp(map(sort_array(ignarr, #'> ),
1139 #'capitalize )
1140 ) + ".",78),
1141 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
1142 0,0,this_object());
1143 return 1;
1144 }
1145 // trim spaces from args and convert to lower case.
1146 str = lower_case(trim(str, TRIM_BOTH));
1147
1148 if (member(ignore, str))
1149 {
1150 RemoveIgnore(str);
1151 tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
1152 }
1153 else if (sizeof(ignore)>100)
1154 {
1155 tell_object(ME, "Du ignorierst schon genuegend!\n");
1156 }
1157 else if (AddIgnore(str) == 1)
1158 {
1159 tell_object(ME,
1160 sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
1161 }
1162 else
1163 {
1164 tell_object(ME,
1165 sprintf("'%s' kannst Du nicht ignorieren.\n",str));
1166 }
1167 return 1;
1168}
1169
1170
Zesstra1cc6cf32022-02-09 11:49:31 +01001171private int _alert_filter_flags(int flag, string text)
Bugfix60f5bc62022-01-21 11:09:56 +01001172{
1173 return (flag & QueryProp(P_ALERT));
1174}
1175
Bugfixc0350dc2025-07-03 14:14:58 +02001176private string beep_status()
1177{
1178 int beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
1179 mapping text = ([
1180 MB_SAY: "sage",
1181 MB_TELL: "teile mit",
1182 MB_CHANNEL: "Ebenenmeldungen",
1183 MB_MENTION: "Erwaehnungen",
1184 MB_SHOUT: "rufe",
1185 MB_MISC: "sonstige",]);
1186 string types = CountUp(map(filter(m_indices(text), #'_alert_filter_flags), text));
1187 if(!sizeof(types))
1188 {
1189 types = "Keine";
1190 }
1191 return
1192 "Gewaehlte Klingeltoene: " + types + "\n"
1193 "Zeitabstand: "
1194 + (beep_interval ? "maximal alle " + beep_interval + " Sekunden."
1195 : "keiner") + "\n"
1196 + (QueryProp(P_ALERT) & AL_NO_SOUND ? " Allerdings sind Toene insgesamt "
1197 "bei Dir abgeschaltet. (\'hilfe ton\')" : "");
1198}
1199
Zesstra1cc6cf32022-02-09 11:49:31 +01001200static int _msg_beep(string str)
1201{
Bugfixc0350dc2025-07-03 14:14:58 +02001202 if(!sizeof(str))
1203 {
1204 ReceiveNotify(
1205 beep_status(),
1206 query_verb());
1207 return 1;
1208 }
Zesstra1cc6cf32022-02-09 11:49:31 +01001209
1210 notify_fail(
1211 "Syntax:\n"
1212 "- klingelton <1 bis 3600 Sekunden>\n"
1213 "- klingelton aus\n"
1214 "- klingelton ein\n"
1215 "- klingelton <+/-><tm / sag / ebenen / ruf / erwaehnung / sonstige / alle / >\n");
1216
Zesstrad536c452025-06-28 14:23:30 +02001217 if (!regmatch(str,"^[[:digit:]]", RE_PCRE))
Bugfix60f5bc62022-01-21 11:09:56 +01001218 {
MG Mud User88f12472016-06-24 23:31:02 +02001219 if (str=="aus")
Zesstra1cc6cf32022-02-09 11:49:31 +01001220 SetProp(P_ALERT, QueryProp(P_ALERT) | AL_NO_SOUND);
1221 else if (str=="ein")
1222 SetProp(P_ALERT, QueryProp(P_ALERT) & ~AL_NO_SOUND);
Bugfix60f5bc62022-01-21 11:09:56 +01001223 else
1224 {
1225 mapping flags = ([
Zesstra1cc6cf32022-02-09 11:49:31 +01001226 "tm": MB_TELL, "teilemit": MB_TELL,
1227 "sag": MB_SAY, "sage": MB_SAY,
1228 "ebene": MB_CHANNEL, "ebenen": MB_CHANNEL,
1229 "ruf": MB_SHOUT, "rufe": MB_SHOUT,
Zesstra3a261e52022-02-10 14:00:31 +01001230 "erwaehnung": MB_MENTION, "erwaehnungen": MB_MENTION,
Zesstra1cc6cf32022-02-09 11:49:31 +01001231 "sonstige": MB_MISC, "sonstiges": MB_MISC,
1232 "alle": MB_ALL, "alles": MB_ALL,
1233 ]);
Bugfix60f5bc62022-01-21 11:09:56 +01001234 foreach(string part : explode(str, " ") - ({""}))
1235 {
Bugfixc0350dc2025-07-03 14:14:58 +02001236 if(!(part[1..] in flags)) return 0;
Bugfix60f5bc62022-01-21 11:09:56 +01001237 if(part[0] == '+')
1238 {
1239 SetProp(P_ALERT,
1240 QueryProp(P_ALERT) | flags[part[1..]]);
1241 }
1242 else if(part[0] == '-')
1243 {
1244 SetProp(P_ALERT,
1245 QueryProp(P_ALERT) & ~ flags[part[1..]]);
1246 }
Bugfixc0350dc2025-07-03 14:14:58 +02001247 else
1248 {
1249 return 0;
1250 }
Bugfix60f5bc62022-01-21 11:09:56 +01001251 }
Bugfix895dc452025-06-30 17:35:45 +02001252 // 0 hat leider historisch die Bedeutung von MB_MISC, daher muss hier
1253 // statt dessen auf AL_NO_SOUND gesetzt werden.
1254 if(!QueryProp(P_ALERT))
1255 {
1256 SetProp(P_ALERT, AL_NO_SOUND);
1257 }
Bugfix60f5bc62022-01-21 11:09:56 +01001258 }
MG Mud User88f12472016-06-24 23:31:02 +02001259 }
Zesstra1cc6cf32022-02-09 11:49:31 +01001260 else
1261 {
Bugfixc0350dc2025-07-03 14:14:58 +02001262 int beep_interval = to_int(str);
Zesstra1cc6cf32022-02-09 11:49:31 +01001263 if(beep_interval >= 0)
1264 {
Bugfixc0350dc2025-07-03 14:14:58 +02001265 SetProp(P_MESSAGE_BEEP, beep_interval);
1266 }
1267 else
1268 {
1269 return 0;
Zesstra1cc6cf32022-02-09 11:49:31 +01001270 }
1271 }
MG Mud User88f12472016-06-24 23:31:02 +02001272
Bugfix60f5bc62022-01-21 11:09:56 +01001273 ReceiveNotify(
Bugfixc0350dc2025-07-03 14:14:58 +02001274 beep_status(),
Bugfix60f5bc62022-01-21 11:09:56 +01001275 query_verb());
MG Mud User88f12472016-06-24 23:31:02 +02001276 return 1;
1277}
1278
1279static int _msg_prepend(string str) {
MG Mud User88f12472016-06-24 23:31:02 +02001280 notify_fail("Syntax: senderwiederholung ein/aus\n");
1281 if (stringp(str)) {
1282 if (str=="aus")
1283 SetProp(P_MESSAGE_PREPEND,1);
1284 else if (str=="ein")
1285 SetProp(P_MESSAGE_PREPEND,0);
1286 else return 0;
1287 }
1288
Bugfixb1d9b4d2021-09-14 20:07:04 +02001289 ReceiveNotify("Senderwiederholung bei Mitteilungen: "+
Zesstra04f613c2019-11-27 23:32:54 +01001290 (({int})QueryProp(P_MESSAGE_PREPEND) ? "aus" : "ein")+".",
MG Mud User88f12472016-06-24 23:31:02 +02001291 query_verb());
1292
1293 return 1;
1294}
1295
1296static int _communicate(mixed str, int silent)
1297{
1298 string verb;
1299 string myname;
1300 string msg;
1301
1302 if (!str || extern_call()) str=_unparsed_args()||"";
1303 /* str=_unparsed_args()||""; */
1304 verb = query_verb();
1305 if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
1306 if (str==""||str==" "||!str)
1307 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001308 ReceiveNotify("Was willst Du sagen?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001309 return 1;
1310 }
1311 msg=permutate(str);
1312
1313 myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
1314 !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
1315 "":QueryProp(P_PRESAY))+name())||"";
1316
1317 // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
1318 // ist, keine MT_COMM)
Zesstrabd1236a2022-02-09 22:35:59 +01001319 send_room(environment(), msg, MT_LISTEN|MSG_ALERT, MA_SAY,
MG Mud User88f12472016-06-24 23:31:02 +02001320 capitalize(myname)+" sagt: ", ({this_object()}) );
1321
1322 if(!silent)
1323 {
1324 ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1325 MA_SAY, "Du sagst: ", ME);
1326 }
1327 return 1;
1328}
1329
1330static int _shout_to_all(mixed str)
1331{
1332 string pre, myname, realname, wizards_msg, players_msg;
1333 string wizard_prefix, player_prefix;
1334 int chars;
1335
1336 if (!(str=_unparsed_args()))
1337 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001338 ReceiveNotify("Was willst Du rufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001339 return 1;
1340 }
1341 chars=sizeof(str)/2;
1342 if (chars<4) chars=4;
1343 pre = (!IS_LEARNER(ME) ||
1344 QueryProp(P_INVIS) ||
1345 !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
1346 realname = capitalize((pre + capitalize(getuid()))||"");
1347 myname = capitalize(pre + name()||"");
1348 if (QueryProp(P_INVIS))
1349 realname = "("+realname+")";
1350
1351 wizards_msg = permutate(str);
1352 wizard_prefix = myname+" ruft: ";
1353
1354 if(QueryProp(P_FROG)) {
1355 players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
1356 player_prefix = myname+" quakt: ";
1357 }
1358 else {
1359 players_msg = wizards_msg;
1360 player_prefix = wizard_prefix;
1361 }
1362
1363 if(!IS_LEARNER(this_player()))
1364 {
1365 if(QueryProp(P_GHOST)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001366 ReceiveNotify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
MG Mud User88f12472016-06-24 23:31:02 +02001367 MA_SHOUT);
1368 return 1;
1369 }
1370 if (QueryProp(P_SP) <(chars+20))
1371 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001372 ReceiveNotify("Du musst erst wieder magische Kraefte sammeln.",
MG Mud User88f12472016-06-24 23:31:02 +02001373 MA_SHOUT);
Bugfixb1d9b4d2021-09-14 20:07:04 +02001374 ReceiveNotify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
MG Mud User88f12472016-06-24 23:31:02 +02001375 "Ebenen').", MA_SHOUT);
1376 return 1;
1377 }
1378 SetProp(P_SP, QueryProp(P_SP) - chars - 20);
1379 }
1380
1381 ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
Zesstrabd1236a2022-02-09 22:35:59 +01001382 MA_SHOUT, "Du rufst: ", ME);
MG Mud User88f12472016-06-24 23:31:02 +02001383
1384 foreach ( object ob : users()-({this_object()}) )
1385 if ( IS_LEARNER(ob) )
Zesstrabd1236a2022-02-09 22:35:59 +01001386 ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR|MSG_ALERT, MA_SHOUT,
1387 wizard_prefix, this_object());
MG Mud User88f12472016-06-24 23:31:02 +02001388 else
Zesstrabd1236a2022-02-09 22:35:59 +01001389 ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR|MSG_ALERT, MA_SHOUT,
1390 player_prefix, this_object());
MG Mud User88f12472016-06-24 23:31:02 +02001391
1392 return 1;
1393}
1394
1395varargs int _tell(string who, mixed msg)
1396{
1397 object ob;
1398 string away,myname,ret;
MG Mud User88f12472016-06-24 23:31:02 +02001399 string *xname;
1400 int i,visflag;
1401
1402 if (extern_call() && this_interactive()!=ME) return 1;
1403 if (!who || !msg) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001404 ReceiveNotify("Was willst Du mitteilen?",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001405 return 1;
1406 }
1407
1408 if(who == ERWIDER_PARAM)
1409 {
1410 if (!last_comm_partner)
1411 {
1412 _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
1413 "Du was erwidern\nkoenntest.\n");
1414 return 0;
1415 }
1416 who=last_comm_partner;
1417 }
1418
1419 // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
1420 if (who == ".")
1421 who = ".1";
1422
1423 if ( sscanf(who, ".%d", i) == 1 ) {
1424 if(i > 0 && i <= sizeof(commreceivers))
1425 who = commreceivers[i-1];
1426 else {
1427 _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
1428 return 0;
1429 }
1430 }
1431
1432 xname = explode(who, "@");
1433
Zesstrabd1236a2022-02-09 22:35:59 +01001434 if (sizeof(xname) == 2)
MG Mud User88f12472016-06-24 23:31:02 +02001435 {
1436 if ( QueryProp(P_QP) )
1437 {
Zesstra04f613c2019-11-27 23:32:54 +01001438 if (ret=({string})INETD->_send_udp(xname[1],
MG Mud User88f12472016-06-24 23:31:02 +02001439 ([ REQUEST: "tell",
1440 RECIPIENT: xname[0],
1441 SENDER: getuid(ME),
1442 DATA: msg ]), 1))
1443 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001444 ReceiveNotify(ret, MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001445 }
1446 else
1447 {
1448 write("Nachricht abgeschickt.\n");
1449 add_to_tell_history(who, 1, 0, msg,
Zesstraa31cd5c2016-12-17 20:11:07 +01001450 "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_RTELL, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001451 }
1452 }
1453 else
1454 write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
1455 "Muds etwas mitteilen zu koennen.\n");
1456 return 1;
1457 }
1458
Zesstra8b5320e2022-02-18 21:10:26 +01001459 string|int lname = lower_case(who);
1460 if (!ob=find_player(lname))
MG Mud User88f12472016-06-24 23:31:02 +02001461 {
Zesstra8b5320e2022-02-18 21:10:26 +01001462 lname = match_living(lname, 0);
1463 if (!stringp(lname))
1464 {
1465 switch(lname)
1466 {
MG Mud User88f12472016-06-24 23:31:02 +02001467 case -1:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001468 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001469 return 1;
1470 case -2:
Zesstra8b5320e2022-02-18 21:10:26 +01001471 // check KOBOLD
Zesstra7459f252022-02-23 22:47:26 +01001472 ob = load_object(KOBOLD);
Zesstra8b5320e2022-02-18 21:10:26 +01001473 lname = ({string|int})ob->find_player(lower_case(who));
1474 if (lname == -1) {
1475 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
1476 return 1;
1477 }
1478 else if (!stringp(lname)) {
1479 ReceiveNotify("Kein solcher Spieler (online)!",MA_TELL);
1480 return 1;
1481 }
MG Mud User88f12472016-06-24 23:31:02 +02001482 }
Zesstra8b5320e2022-02-18 21:10:26 +01001483 // jetzt ist ob der KOBOLD und lname die UID eines Spielers.
1484 }
1485 // Wenn wir noch kein ob haben, sollte lname jetzt aber durch das
1486 // match_living() der Name eines Livings sein, dessen Objekt wir noch
1487 // brauchen.
1488 if (!ob)
1489 ob = find_player(lname) || find_living(lname);
MG Mud User88f12472016-06-24 23:31:02 +02001490 if(!ob) {
Zesstra8b5320e2022-02-18 21:10:26 +01001491 ReceiveNotify("Kein solcher Spieler (online)!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001492 return 1;
1493 }
1494 }
1495
Zesstra8b5320e2022-02-18 21:10:26 +01001496 if(QueryProp(P_INVIS))
1497 {
MG Mud User88f12472016-06-24 23:31:02 +02001498 if(!IS_LEARNER(ob))
1499 myname = name();
1500 else
1501 myname="("+
1502 ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
1503 capitalize(getuid()) + ")";
1504 }
1505 else
1506 myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
Bugfix45f88ce2017-03-06 14:41:56 +01001507 QueryProp(P_PRESAY):"") + capitalize(getuid(ME));
Zesstra8b5320e2022-02-18 21:10:26 +01001508 if (myname && sizeof(myname))
1509 myname=capitalize(myname);
1510
1511 // Wir haben Zielobjekt und unseren eigenen Namen. Erstmal an Empfaenger
1512 // senden.
1513 if (living(ob))
1514 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_ALERT, MA_TELL,
1515 myname + " teilt Dir mit: ");
1516 else
1517 _send_to_kobold(lname, permutate(msg), MT_COMM|MT_FAR|MSG_ALERT, MA_TELL,
1518 myname + " teilt Dir mit: ");
MG Mud User88f12472016-06-24 23:31:02 +02001519
1520 // dann evtl. noch an Absender ausgeben...
Zesstra8b5320e2022-02-18 21:10:26 +01001521 visflag = !ob->QueryProp(P_INVIS);
1522 if (visflag || IS_LEARNER(this_player()))
1523 _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(lname) + " mit: ");
MG Mud User88f12472016-06-24 23:31:02 +02001524 // oder irgendwas anderes an den Absender ausgeben...
1525 if (!visflag && interactive(ob))
Zesstra8b5320e2022-02-18 21:10:26 +01001526 ReceiveNotify("Kein solcher Spieler online!",MA_TELL);
Zesstra04f613c2019-11-27 23:32:54 +01001527 else if (away = ({string})ob->QueryProp(P_AWAY))
Zesstra8b5320e2022-02-18 21:10:26 +01001528 ReceiveMsg( break_string( away, 78, capitalize(lname)
MG Mud User88f12472016-06-24 23:31:02 +02001529 + " ist gerade nicht da: ", BS_INDENT_ONCE ),
1530 MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
1531 MA_TELL, 0, this_object());
1532 else if (interactive(ob) && (i=query_idle(ob))>=600)
1533 { //ab 10 Mins
1534 if (i<3600)
1535 away=time2string("%m %M",i);
1536 else
1537 away=time2string("%h %H und %m %M",i);
1538
Bugfixb1d9b4d2021-09-14 20:07:04 +02001539 ReceiveNotify(sprintf("%s ist seit %s voellig untaetig.",
Zesstra8b5320e2022-02-18 21:10:26 +01001540 capitalize(lname),away),
MG Mud User88f12472016-06-24 23:31:02 +02001541 MA_TELL);
1542 }
1543
1544 return 1;
1545}
1546
1547static int _teile(string str)
1548{
1549 string who, message;
1550 if (!(str=_unparsed_args())) return 0;
1551 if (sscanf(str, "%s mit %s", who, message) == 2)
1552 return _tell(who, message,1);
1553 return 0;
1554}
1555static int _teile_mit_alias(string str)
1556{
1557 str = _unparsed_args(), TRIM_LEFT;
1558 if (!str) return 0;
1559 str = trim(str, TRIM_LEFT);
1560 // Ziel muss min. 2 Buchstaben haben (.<nr>)
1561 if (sizeof(str) < 4) return 0;
1562 int pos = strstr(str, " ");
1563 if (pos >= 2)
1564 return _tell(str[..pos-1], str[pos+1..]);
1565 return 0;
1566}
1567
1568static int _erzaehle(string str)
1569{
1570 string who, message;
1571
1572 if (!(str=_unparsed_args())) return 0;
1573 if (sscanf(str, "%s %s", who, message) == 2)
1574 return _tell(who, message,1);
1575 return 0;
1576}
1577
1578static int _whisper(string str)
1579{
1580 object ob;
1581 string who;
1582 string msg;
1583 string myname;
1584
1585 if (!(str=_unparsed_args()) ||
1586 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1587 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001588 ReceiveNotify("Was willst Du wem zufluestern?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001589 return 1;
1590 }
Zesstra6e88b6a2019-11-08 00:25:39 +01001591 who = lower_case(who);
MG Mud User88f12472016-06-24 23:31:02 +02001592 if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001593 ReceiveNotify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001594 return 1;
1595 }
1596
1597 myname = capitalize((((IS_LEARNER(ME) &&
1598 !QueryProp(P_INVIS) &&
1599 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1600 QueryProp(P_PRESAY) : "") + name()) || "");
1601
1602 _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
Bugfixdbdbed52022-01-21 11:14:35 +01001603 MA_SAY, myname + " fluestert Dir zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001604 send_room(environment(),
1605 myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
1606 MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
1607
1608 _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
1609
1610
1611 return 1;
1612}
1613
1614static int _remote_whisper(string str)
1615{
1616 /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
1617 Nachricht nicht in der tell_history verewigt. */
1618
1619 object ob;
Zesstra8b5320e2022-02-18 21:10:26 +01001620 string who;
MG Mud User88f12472016-06-24 23:31:02 +02001621 string msg;
1622 string myname;
1623
1624 if (!(str=_unparsed_args()) ||
1625 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1626 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001627 ReceiveNotify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001628 return 1;
1629 }
1630
Zesstra8b5320e2022-02-18 21:10:26 +01001631 string|int lname = lower_case(who);
1632 if (!ob=find_player(lname))
MG Mud User88f12472016-06-24 23:31:02 +02001633 {
Zesstra8b5320e2022-02-18 21:10:26 +01001634 lname = match_living(lname, 0);
1635 if (!stringp(lname))
1636 {
1637 switch(lname)
1638 {
MG Mud User88f12472016-06-24 23:31:02 +02001639 case -1:
Zesstra8b5320e2022-02-18 21:10:26 +01001640 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001641 return 1;
1642 case -2:
Zesstra8b5320e2022-02-18 21:10:26 +01001643 // rfluester benutzt keinen KOBOLD.
1644 ReceiveNotify("Kein solcher Spieler!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001645 return 1;
1646 }
Zesstra8b5320e2022-02-18 21:10:26 +01001647 }
1648 // lname sollte nun eindeutig sein.
1649 ob = find_player(lname) || find_living(lname);
1650 if(!ob) {
1651 ReceiveNotify("Kein solcher Spieler online!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001652 return 1;
1653 }
1654 }
Zesstra8b5320e2022-02-18 21:10:26 +01001655
MG Mud User88f12472016-06-24 23:31:02 +02001656 if (environment(ob) == environment()) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001657 ReceiveNotify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001658 return 1;
1659 }
1660
1661 myname = capitalize((((IS_LEARNER(ME) &&
1662 !QueryProp(P_INVIS) &&
1663 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1664 QueryProp(P_PRESAY) : "") + name()) || "");
1665
1666 // An Empfaenger senden.
Zesstrabd1236a2022-02-09 22:35:59 +01001667 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE,
1668 MA_EMOTE, myname + " fluestert Dir aus der Ferne zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001669 // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
1670 if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
Zesstra8b5320e2022-02-18 21:10:26 +01001671 ReceiveNotify("Kein solcher Spieler online!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001672 return 1;
1673 }
1674 // sonst eigene Meldung via _recv() ausgeben.
1675 else
1676 _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
1677 "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
1678
1679 return 1;
1680}
1681
1682static int _converse(string arg)
1683{
Bugfixb1d9b4d2021-09-14 20:07:04 +02001684 ReceiveNotify("Mit '**' wird das Gespraech beendet.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001685 if (stringp(arg) && strstr(arg, "-s") == 0)
1686 input_to("_converse_more", INPUT_PROMPT, "]", 1);
1687 else
1688 input_to("_converse_more", INPUT_PROMPT, "]", 0);
1689 return 1;
1690}
1691
1692static int _converse_more(mixed str, int silent)
1693{
1694 if (str == "**") {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001695 ReceiveNotify("Ok.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001696 return 0;
1697 }
1698
1699 if(str != "")
1700 _communicate(str, silent);
1701
1702 input_to("_converse_more", INPUT_PROMPT, "]", silent);
1703 return 1;
1704}
1705
1706private int is_learner(object o) { return IS_LEARNER(o); }
1707
1708static int _shout_to_wizards(mixed str)
1709{
MG Mud User88f12472016-06-24 23:31:02 +02001710 string myname;
MG Mud User88f12472016-06-24 23:31:02 +02001711
1712 str = _unparsed_args();
1713 if (!str||!sizeof(str)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001714 ReceiveNotify("Was willst Du den Magiern zurufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001715 return 1;
1716 }
1717 // Kontrollzeichen rausfiltern.
1718 str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
1719 myname = capitalize(getuid(this_object()));
1720 if (!IS_LEARNER(this_object()))
1721 _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
1722
1723 // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
1724 filter(users(), #'is_learner)->ReceiveMsg(str,
Zesstrabd1236a2022-02-09 22:35:59 +01001725 MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_ALERT,
MG Mud User88f12472016-06-24 23:31:02 +02001726 MA_SHOUT, myname+" an alle Magier: ", this_object());
1727
1728 return 1;
1729}
1730
1731static int _echo(string str) {
1732 if (!IS_SEER(ME) || (!IS_LEARNER(ME)
1733 && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
1734 return 0;
1735
1736 if (!(str=_unparsed_args())) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001737 ReceiveNotify("Was moechtest Du 'echoen'?", 0);
MG Mud User88f12472016-06-24 23:31:02 +02001738 return 1;
1739 }
1740
1741 if (!IS_LEARNER(this_interactive()))
1742 {
1743 if (QueryProp(P_GHOST))
1744 {
1745 _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
1746 return 0;
1747 }
1748 if (QueryProp(P_SP)<ECHO_COST)
1749 {
1750 _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
1751 return 0;
1752 }
1753 SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
1754 str=">\b"+str;
1755 log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
1756 str));
1757 }
1758 // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
1759 // Kommhistory noch im Kobold landen.
1760 send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
1761 MA_UNKNOWN, 0, 0);
1762 return 1;
1763}
1764
1765// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
1766// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
1767// und gefedert.
1768static string *_set_ignore(mixed arg)
1769{
1770 raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
1771 "Benutze AddIgnore/RemoveIgnore()!\n");
1772}
1773// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
1774// nix dran, dass alle TestIgnore() & Co benutzen sollen.
1775static string *_query_ignore() {
1776 mixed ign=Query(P_IGNORE, F_VALUE);
1777 if (mappingp(ign))
1778 return m_indices(ign);
1779 return ({});
1780}
1781
1782public int AddIgnore(string ign) {
1783 // Einige strings sind nicht erlaubt, z.B. konsekutive .
1784 if (!sizeof(ign)
1785 || regmatch(ign,"[.]{2,}",RE_PCRE)
1786 || regmatch(ign," ",RE_PCRE)
1787 || sizeof(explode(ign,"."))>3)
1788 return 0;
1789
1790 mapping ignores=Query(P_IGNORE, F_VALUE);
1791 ignores[ign]=time();
1792 // kein Set() noetig.
1793 return 1;
1794}
1795
1796public int RemoveIgnore(string ign)
1797{
1798 mapping ignores=Query(P_IGNORE,F_VALUE);
1799 m_delete(ignores,ign);
1800 // Kein Set() noetig
1801 return 1;
1802}
1803
MG Mud User88f12472016-06-24 23:31:02 +02001804int erwidere(string str)
1805{
1806 str=_unparsed_args();
1807 if (!str) return 0;
1808 return _tell(ERWIDER_PARAM, str ,1);
1809}
1810
1811static int tmhist(string str)
1812{
1813
1814 if (str == "aus") {
1815 tell_history_enabled = TELLHIST_DISABLED;
1816 write("Ok, es wird nichts mehr gespeichert.\n");
1817 if (sizeof(tell_history)) {
Zesstra996bafe2022-02-14 22:42:39 +01001818 clear_tell_history(1);
MG Mud User88f12472016-06-24 23:31:02 +02001819 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1820 }
1821 return 1;
1822 }
Zesstra996bafe2022-02-14 22:42:39 +01001823 if (str == "loeschen")
1824 {
1825 if (sizeof(tell_history)) {
1826 clear_tell_history(1);
1827 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1828 }
1829 else
1830 write("Deine Mitteilungsgeschichte war schon leer.\n");
1831 return 1;
1832 }
MG Mud User88f12472016-06-24 23:31:02 +02001833
1834 if (str == "namen") {
1835 int flag;
1836 tell_history_enabled = TELLHIST_NO_MESSAGE;
1837 write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
1838 foreach (string uid, struct chat_s chat: tell_history)
1839 if (pointerp(chat->msgbuf)) {
1840 chat->msgbuf = 0;
1841 chat->ptr = 0;
1842 flag = 1;
1843 }
1844 if (flag)
1845 write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
1846 return 1;
1847 }
1848
1849 if (str == "ein" || str == "an") {
1850 tell_history_enabled = TELLHIST_ENABLED;
1851 write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
1852 return 1;
1853 }
1854
MG Mud User88f12472016-06-24 23:31:02 +02001855 if (str == "langlebig") {
1856 tell_history_enabled = TELLHIST_LONGLIFE;
1857 write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
1858 "Ende/Crash/\nReboot gespeichert.\n");
1859 return 1;
1860 }
MG Mud User88f12472016-06-24 23:31:02 +02001861
1862 if (str == "status") {
1863 switch (tell_history_enabled) {
1864 case TELLHIST_DISABLED:
1865 write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
1866 break;
1867 case TELLHIST_NO_MESSAGE:
1868 write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
1869 break;
1870 case TELLHIST_ENABLED:
1871 write("Deine Mitteilungen werden gespeichert.\n");
1872 break;
MG Mud User88f12472016-06-24 23:31:02 +02001873 case TELLHIST_LONGLIFE:
1874 write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
1875 "Crash/Reboot\ngespeichert.\n");
1876 break;
MG Mud User88f12472016-06-24 23:31:02 +02001877 }
1878 return 1;
1879 }
1880
1881 if (tell_history_enabled == TELLHIST_DISABLED) {
1882 _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
1883 return 0;
1884 }
1885
1886 if (!sizeof(tell_history)) {
1887 _notify_fail("Du hast noch keinem etwas mitgeteilt "
1888 "und noch keine Mitteilungen erhalten.\n");
1889 return 0;
1890 }
1891
1892 if (str && sizeof(str)) {
1893
1894 if (tell_history_enabled < TELLHIST_ENABLED) {
1895 _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
1896 return 0;
1897 }
1898
1899 string uid;
1900 if (member(tell_history, str)) {
1901 // Name gewuenscht, da der String in der History vorkommt.
1902 uid = str;
1903 }
1904 else {
1905 // evtl. ne Zahl angegeben.
1906 int i;
1907 string *partners = sorted_commpartners(0);
1908 if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
1909 uid = partners[i];
1910 else {
1911 notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
1912 return 0;
1913 }
1914 }
1915
1916 mixed *data = tell_history[uid]->msgbuf;
1917 if (!data) {
1918 _notify_fail(
1919 "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
1920 return 0;
1921 }
1922
1923 int ptr = tell_history[uid]->ptr;
1924
1925 More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
1926 data[0..ptr-1],
Zesstra7459f252022-02-23 22:47:26 +01001927 function string (struct kobold_msg_s msg) {
MG Mud User88f12472016-06-24 23:31:02 +02001928 if (!structp(msg)) return "";
Zesstra3a261e52022-02-10 14:00:31 +01001929 return break_string(terminal_colour(msg->msg, colourmap)
Zesstra7459f252022-02-23 22:47:26 +01001930 + " ["
1931 + strftime("%H:%M:%S",msg->timestamp) + "]", 78,
MG Mud User88f12472016-06-24 23:31:02 +02001932 msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
1933 } ) ) );
1934 return 1;
1935 }
1936
1937 string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
1938 int i;
1939 foreach (string uid : sorted_commpartners(0) ) {
1940 int j;
1941 struct chat_s chat = tell_history[uid];
1942 history += sprintf("%2d.%-4s %s %-11s %d gesendet/%d empfangen\n", ++i,
1943 ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
1944 strftime("%a, %e.%m.%y",chat->time_last_msg),
1945 capitalize(chat->uid), chat->sentcount, chat->recvcount);
1946 }
1947
1948 More(history);
1949
1950 return 1;
1951}
1952
1953static mixed _query_localcmds()
1954{
1955 return ({
1956 ({"kobold", "cmd_kobold",0,0}),
1957 ({"sag","_communicate",0,0}),
1958 ({"sage","_communicate",0,0}),
1959 ({"'","_communicate",1,0}),
1960 ({"mruf","_shout_to_wizards",0,0}),
1961 ({"mrufe","_shout_to_wizards",0,0}),
1962 ({"ruf","_shout_to_all",0,0}),
1963 ({"rufe","_shout_to_all",0,0}),
1964 ({"erzaehl","_erzaehle",0,0}),
1965 ({"erzaehle","_erzaehle",0,0}),
1966 ({"teil","_teile",0,0}),
1967 ({"teile","_teile",0,0}),
1968 ({"tm","_teile_mit_alias",0,0}),
1969 ({"fluester","_whisper",0,0}),
1970 ({"fluestere","_whisper",0,0}),
1971 ({"rfluester","_remote_whisper",0,0}),
1972 ({"rfluestere","_remote_whisper",0,0}),
1973 ({"gespraech","_converse",0,0}),
1974 ({"echo","_echo",0,0}),
1975 ({"ignorier","ignoriere",0,0}),
1976 ({"ignoriere","ignoriere",0,0}),
1977 ({"tmhist","tmhist",0,0}),
1978 ({"erwider","erwidere",0,0}),
1979 ({"erwidere","erwidere",0,0}),
1980 ({"klingelton","_msg_beep",0,0}),
1981 ({"senderwiederholung","_msg_prepend",0,0}),
1982 ({"report","set_report",0,0}),
1983 })+channel::_query_localcmds();
1984}
1985
1986private string *sorted_commpartners(int reversed) {
1987 return sort_array(m_indices(tell_history),
1988 function int (string uid1, string uid2) {
1989 if (reversed)
1990 return tell_history[uid1]->time_last_msg >
1991 tell_history[uid2]->time_last_msg;
1992 else
1993 return tell_history[uid1]->time_last_msg <=
1994 tell_history[uid2]->time_last_msg;
1995 } );
1996}
1997
Zesstra0712bac2020-06-12 09:41:24 +02001998// Setzt den Prompt eines Interactives. Gerufen bei Objekterstellung,
1999// Reconnect und bei Magiern, wenn diese ihren Prompt oder ihr aktuelles
2000// Verzeichnis aendern.
MG Mud User88f12472016-06-24 23:31:02 +02002001static void modify_prompt() {
2002 string text = Query(P_PROMPT, F_VALUE);
2003
2004 if ( !stringp(text) || !sizeof(text) )
2005 text = "> ";
2006 else {
2007 string path = Query(P_CURRENTDIR, F_VALUE);
2008 if (stringp(path) && sizeof(path))
2009 text = regreplace(text,"\\w",path,0); // Pfad einsetzen
2010 }
2011 configure_interactive(this_object(), IC_PROMPT, text);
2012}
2013
2014// Prueft auf Ingoriereeintraege.
2015// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
MG Mud User88f12472016-06-24 23:31:02 +02002016public int TestIgnore(string|string* srcnames)
MG Mud User88f12472016-06-24 23:31:02 +02002017{
2018 mapping ign = Query(P_IGNORE, F_VALUE);
2019 if (stringp(srcnames))
2020 srcnames = ({srcnames});
2021
2022 foreach(string srcname: srcnames)
2023 {
2024 // einfachster Fall, exakter Match
2025 if (member(ign, srcname))
2026 return MSG_IGNORED;
2027 // ansonsten muss aufgetrennt werden.
2028 if (strstr(srcname,".") > -1)
2029 {
2030 string *srcparts=explode(srcname,".");
2031 switch(sizeof(srcparts))
2032 {
2033 // case 0 und 1 kann nicht passieren.
2034 case 3:
2035 // zu pruefen: [sender].aktion.qualifizierer.
2036 // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
2037 // oben schon geprueft. im Spieler geprueft werden muss noch:
2038 // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
2039 if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
2040 || member(ign, "."+srcparts[1]) // .aktion
2041 || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
2042 || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
2043 )
2044 {
2045 return MSG_IGNORED;
2046 }
2047 break;
2048 case 2:
2049 // zu pruefen: spieler.aktion
2050 // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
2051 // oben schon geprueft. Im Spieler zu pruefen ist noch:
2052 // spieler und .aktion
2053 if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
2054 || member(ign, "."+srcparts[1]))
2055 {
2056 return MSG_IGNORED;
2057 }
2058 break;
2059 default: // mehr als 3 Teile...
2060 raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
2061 "is supported. Got: %s\n",srcname));
MG Mud User88f12472016-06-24 23:31:02 +02002062 }
2063 }
2064 }
2065 // Default: nicht ignorieren.
2066 return 0;
2067}
2068
2069#ifdef __LPC_UNIONS__
2070public int TestIgnoreExt(string|string* srcnames)
2071#else
2072public int TestIgnoreExt(mixed srcnames)
2073#endif
2074{
2075 return TestIgnore(srcnames);
2076}
2077
2078// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
2079// Typen.
2080// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
2081private int check_ignores(string msg, int msg_type, string msg_action,
Zesstra7459f252022-02-23 22:47:26 +01002082 string msg_prefix, object|string origin)
MG Mud User88f12472016-06-24 23:31:02 +02002083{
2084 // Einige Dinge lassen sich nicht ignorieren.
2085 if (msg_type & (MT_NEWS|MT_NOTIFICATION))
2086 return 0;
2087 // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
2088 // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
2089 // eine ignorierbare msg_action geben.
2090 else if (stringp(msg_action) && origin && origin != ME)
2091 {
Zesstra7459f252022-02-23 22:47:26 +01002092 string srcname;
2093 if (objectp(origin))
2094 srcname =
MG Mud User88f12472016-06-24 23:31:02 +02002095 (query_once_interactive(origin) ? origin->query_real_name()
2096 : origin->name(WER) || "");
Zesstra7459f252022-02-23 22:47:26 +01002097 else
2098 srcname = origin;
2099
MG Mud User88f12472016-06-24 23:31:02 +02002100 mapping ign = Query(P_IGNORE, F_VALUE);
2101
2102 if (member(ign, srcname))
2103 return MSG_IGNORED;
2104 // vielleicht wird irgendwas a la name.aktion ignoriert?
2105 // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
2106 // Ebenenmeldungen)
2107 if (member(ign, srcname+"."+msg_action))
2108 return MSG_VERB_IGN;
2109 // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
2110 // das reichlich sinnfrei ist.
2111 if (member(ign, "."+msg_action))
2112 return MSG_VERB_IGN;
2113 // Spieler auf Ebenen ignoriert?
2114 // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
2115 if (strstr(msg_action, MA_CHANNEL) == 0)
2116 {
2117 // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
2118 if (member(ign, srcname + "."MA_CHANNEL))
2119 return MSG_IGNORED;
2120 // spieler.ebenen.ebenenname ist oben schon abgedeckt.
2121 // .ebenen halte ich fuer sinnfrei, nicht geprueft.
2122 }
2123 // Spieler aus anderem mud? *seufz*
2124 if (strstr(srcname,"@") > -1)
2125 {
2126 string *srcparts = explode(srcname,"@");
2127 if (sizeof(srcparts)==2)
2128 {
2129 // spieler@?
2130 if (member(ign, srcparts[0]+"@"))
2131 return MSG_IGNORED;
2132 // oder Mud per @mud?
2133 if (member(ign, "@" + srcparts[1]))
2134 return MSG_MUD_IGN;
2135 // BTW: spieler@mud wurde schon ganz oben erfasst.
2136 }
2137 }
2138 }
2139 // Default: nicht ignorieren.
2140 return 0;
2141}
2142
2143// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
2144// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
Bugfixe0fc68f2022-01-07 15:30:54 +01002145// BLindheit/Taubheit). In Zukunft koennten aber weitere Typen und weitere
2146// Kriterien wichtig werden und weitere Argumente uebergeben werden. (Dies
2147// bitte beachten, falls diese Funktion protected/public werden sollte.)
MG Mud User88f12472016-06-24 23:31:02 +02002148// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
2149// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
2150// Sinn gar nix zu tun hat.
2151// Rueckgabe: 0 oder MSG_SENSE_BLOCK
Bugfixe0fc68f2022-01-07 15:30:54 +01002152private int check_senses(int msg_type)
MG Mud User88f12472016-06-24 23:31:02 +02002153{
2154 int senses = msg_type & (MT_LOOK|MT_LISTEN);
2155 // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
2156 // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
2157 // unterdrueckt werden.
2158 if (!senses)
2159 return 0;
Bugfix427a5812022-01-07 15:41:24 +01002160
2161 int orig_senses = senses;
2162
MG Mud User88f12472016-06-24 23:31:02 +02002163 if ((senses & MT_LOOK) && CannotSee(1))
2164 senses &= ~MT_LOOK; // Sinn loeschen
2165
2166 if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
2167 senses &= ~MT_LISTEN;
2168
Bugfix427a5812022-01-07 15:41:24 +01002169 // Wenn kein Sinn mehr ueber ist oder all_types gesetzt ist und nicht mehr
2170 // alle Sinne vorhanden sind, wird die Nachricht nicht wahrgenommen.
2171 if (orig_senses == senses // kein Sinn geloescht, haeufigster Fall
2172 || (!(msg_type&MSG_ALL_SENSES) && senses) ) // min. ein Sinn uebrig
2173 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02002174
Bugfix427a5812022-01-07 15:41:24 +01002175 return MSG_SENSE_BLOCK;
MG Mud User88f12472016-06-24 23:31:02 +02002176}
2177
Zesstra09bcf042023-12-30 20:07:19 +01002178public int ReceiveMsg(string msg, int msg_type, string msg_action,
MG Mud User88f12472016-06-24 23:31:02 +02002179 string msg_prefix, object origin)
2180{
2181 if (!msg) return MSG_FAILED;
2182
2183 // Flags und Typen spalten
2184 int flags = msg_type & MSG_ALL_FLAGS;
2185 int type = msg_type & ~flags;
2186
Zesstra09bcf042023-12-30 20:07:19 +01002187 // msg_action kommt per Defaultargument, msg_type hat per Default 0, daher
2188 // ggf. auch noch den Typ raten (leider geht msg_type nicht als
2189 // Defaultargument, da es *nach* msg_action ausgewertet werden muss, aber in
2190 // der Argumentliste leider *vor* msg_action kommt...
MG Mud User88f12472016-06-24 23:31:02 +02002191 type ||= comm_guess_message_type(msg_action, origin);
2192
2193 // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
2194 if (msg_type & MT_DEBUG)
2195 {
2196 if (!QueryProp(P_WIZ_DEBUG)
2197 || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
2198 return MSG_FAILED;
2199 }
2200
2201 // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
2202 // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
2203 // Weiterbearbeitung.
2204 // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
2205 if (!(flags & MSG_DONT_IGNORE))
2206 {
Bugfix427a5812022-01-07 15:41:24 +01002207 // Sinne pruefen.
2208 int res=check_senses(msg_type);
MG Mud User88f12472016-06-24 23:31:02 +02002209 if (res) return res;
2210
2211 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
2212 res=check_ignores(msg, type, msg_action, msg_prefix, origin);
2213 if (res) return res;
2214 }
2215
Zesstra3a261e52022-02-10 14:00:31 +01002216 // Mentions in der Form @Charname werden in Kommunikation oder beim Hoeren
2217 // von Rufen und Sagen markiert und gegebenfalls gibt es ein Pieps dafuer
2218 int mention;
2219 if ((type & MT_COMM)
2220 || ((type & MT_LISTEN) && msg_action in ({MA_SAY, MA_SHOUT}))
2221 )
2222 {
2223 // Um den Charnamen Tags fuer terminal_colour() einfuegen.
2224 // Lookahead und Lookbehind assertions um die Whitespaces um das Wort
2225 // nicht in den Match einzuschliessen (und zu ersetzen).
2226 string tmp = regreplace(msg,
Zesstra88cf8172025-06-28 13:19:53 +02002227 sprintf("(?<=\\s){0,1}@%s(?=\\s*){0,1}",getuid(ME)),
Zesstra3a261e52022-02-10 14:00:31 +01002228 sprintf("%%^mention%%^@%s%%^normal%%^",
2229 capitalize(getuid(ME))),
2230 RE_PCRE|RE_GLOBAL|RE_CASELESS);
2231 send_debug(ME, tmp);
2232 // Der Vergleich ist weniger schlimm als es aussieht, weil die Strings
2233 // unterschiedlich gross sein werden und daher nicht zeichenweise
2234 // verglichen werden muessen. Es ist dann jedenfalls schneller als
2235 // getrennt mit regmatch oder strstr zu schauen, ob @charname
2236 // vorkommt.
2237 if (tmp != msg)
2238 {
2239 msg = tmp;
2240 mention = 1;
2241 }
2242 }
2243
MG Mud User88f12472016-06-24 23:31:02 +02002244 // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
2245 if ((type & MT_COMM))
2246 {
2247 // erstmal in der Komm-History ablegen, wenn gewuenscht.
2248 if ((!(flags & MSG_DONT_STORE)))
2249 {
2250 string uid;
2251 if (query_once_interactive(origin))
2252 uid = origin->query_real_name();
2253 else
2254 uid = origin->name(WER) || "<unbekannt>";
Zesstraa31cd5c2016-12-17 20:11:07 +01002255 add_to_tell_history(uid, 0, 1, msg, msg_prefix,
2256 (msg_action == MA_TELL ? MSGFLAG_TELL : 0 ) );
MG Mud User88f12472016-06-24 23:31:02 +02002257 }
2258
2259 // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
2260 // Ebenenmeldungen. (Die haben ggf. schon.)
2261 if (stringp(msg_action) && QueryProp(P_AWAY)
2262 && strstr(msg_action, MA_CHANNEL) != 0)
2263 {
2264 // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
2265 if (msg[<1] == '\n')
2266 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
2267 else
2268 msg = msg + " [" + strftime("%H:%M",time()) + "]";
2269 }
2270 // Kobold erlaubt und gewuenscht? Kobold ist fuer die
2271 // direkte Kommunikation mittels MT_COMM vorgesehen.
2272 // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
2273 // ignorieren.
2274 // die if-Konstruktion ist so, weil ich das _flush_cache() im else
2275 // brauche.
2276 if (query_editing(this_object()) || query_input_pending(this_object())
2277 || QueryProp(P_EARMUFFS))
2278 {
2279 if (!(flags & MSG_DONT_BUFFER)
Zesstra7459f252022-02-23 22:47:26 +01002280 && (QueryProp(P_BUFFER) & KOBOLD_ONLINE))
MG Mud User88f12472016-06-24 23:31:02 +02002281 {
2282 // Nachricht soll im Kobold gespeichert werden.
2283 return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
2284 }
2285 }
2286 else
2287 {
2288 // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
2289 // entleeren.
2290 _flush_cache(0);
2291 }
Bugfix3afcb792022-01-21 22:32:42 +01002292 }
2293
Zesstra3a261e52022-02-10 14:00:31 +01002294 // Farbtags ersetzen
2295 msg = terminal_colour(msg, colourmap);
2296
2297 // Alertton senden?
2298 if ((msg_type & MSG_ALERT) || mention)
2299 comm_beep(msg_action, mention);
MG Mud User88f12472016-06-24 23:31:02 +02002300
2301 // Ausgabenachricht bauen und an den Spieler senden.
2302 if (flags & MSG_DONT_WRAP)
2303 msg = (msg_prefix ? msg_prefix : "") + msg;
2304 else
2305 {
2306 int bsflags = flags & MSG_ALL_BS_FLAGS;
2307 if (QueryProp(P_MESSAGE_PREPEND))
2308 bsflags |= BS_PREPEND_INDENT;
2309 msg = break_string(msg, 78, msg_prefix, bsflags);
2310 }
2311 efun::tell_object(ME, msg);
2312
2313 return MSG_DELIVERED;
2314}
Zesstra7459f252022-02-23 22:47:26 +01002315
2316// called from base.c in Reconnect()
2317protected void reconnect() {
2318 // Cache fuer den report zuruecksetzen, der koennte veraltet sein (insb.
2319 // falls in der letzten Session GMCP benutzt wurde und jetzt nicht).
2320 report_cache = 0;
2321}
2322
2323protected void updates_after_restore(int newflag) {
Zesstrafeb5d282025-08-02 14:26:27 +02002324 // SAVE-Flag einer praehistorischen Property P_INTERMUD entfernen.
2325 Set("intermud", SAVE, F_MODE_AD);
2326
Zesstra7459f252022-02-23 22:47:26 +01002327 // Colourmap aktualisieren nach Restore
2328 colourmap = build_colourmap(QueryProp(P_TTY));
2329
2330 // Altes Ignoriere loeschen...
2331 mixed ign = Query(P_IGNORE,F_VALUE);
2332 if (!mappingp(ign))
2333 {
2334 if (pointerp(ign))
2335 ReceiveNotify(break_string(
2336 "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
2337 "weil es eine Aktualisierung der Ignorierefunktion gab, "
2338 "bei der eine Konversion der Daten leider nicht "
2339 "moeglich war.",78), 0);
2340
2341 Set(P_IGNORE, ([]), F_VALUE);
2342 }
2343 // ggf. Comm-Vault abrufen oder neu erstellen.
2344 setup_comm_vault();
2345 // Wenn es eins gibt, den Inhalt zu unserem internen Koboldpuffer
2346 // hinzufuegen, von wo es spaeter angezeigt wird.
2347 if (commvault)
2348 process_comm_vault(commvault);
Bugfix895dc452025-06-30 17:35:45 +02002349
Zesstra47bff292025-07-08 21:40:05 +02002350 // P_ALERT korrigieren. Das historische Originalverhalten war, bei P_ALERT
2351 // == 0 alle Klingeltoene abzuspielen. Default fuer heute soll die gezielte
2352 // Kommunikation an den Spieler einschliessen.
Bugfix895dc452025-06-30 17:35:45 +02002353 if(!QueryProp(P_ALERT))
2354 {
Zesstra47bff292025-07-08 21:40:05 +02002355 SetProp(P_ALERT, MB_TELL|MB_MENTION|MB_MISC);
Bugfix895dc452025-06-30 17:35:45 +02002356 }
Zesstra7459f252022-02-23 22:47:26 +01002357}
2358