blob: e6c871882dcb710bec4f245fb5ad0fa1a7bce225 [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
Zesstra1cc6cf32022-02-09 11:49:31 +01001178static int _msg_beep(string str)
1179{
1180 int beep_interval;
1181
1182 notify_fail(
1183 "Syntax:\n"
1184 "- klingelton <1 bis 3600 Sekunden>\n"
1185 "- klingelton aus\n"
1186 "- klingelton ein\n"
1187 "- klingelton <+/-><tm / sag / ebenen / ruf / erwaehnung / sonstige / alle / >\n");
1188
1189 if(!sizeof(str))
1190 return 0;
Zesstrad536c452025-06-28 14:23:30 +02001191 if (!regmatch(str,"^[[:digit:]]", RE_PCRE))
Bugfix60f5bc62022-01-21 11:09:56 +01001192 {
MG Mud User88f12472016-06-24 23:31:02 +02001193 if (str=="aus")
Zesstra1cc6cf32022-02-09 11:49:31 +01001194 SetProp(P_ALERT, QueryProp(P_ALERT) | AL_NO_SOUND);
1195 else if (str=="ein")
1196 SetProp(P_ALERT, QueryProp(P_ALERT) & ~AL_NO_SOUND);
Bugfix60f5bc62022-01-21 11:09:56 +01001197 else
1198 {
1199 mapping flags = ([
Zesstra1cc6cf32022-02-09 11:49:31 +01001200 "tm": MB_TELL, "teilemit": MB_TELL,
1201 "sag": MB_SAY, "sage": MB_SAY,
1202 "ebene": MB_CHANNEL, "ebenen": MB_CHANNEL,
1203 "ruf": MB_SHOUT, "rufe": MB_SHOUT,
Zesstra3a261e52022-02-10 14:00:31 +01001204 "erwaehnung": MB_MENTION, "erwaehnungen": MB_MENTION,
Zesstra1cc6cf32022-02-09 11:49:31 +01001205 "sonstige": MB_MISC, "sonstiges": MB_MISC,
1206 "alle": MB_ALL, "alles": MB_ALL,
1207 ]);
Bugfix60f5bc62022-01-21 11:09:56 +01001208 foreach(string part : explode(str, " ") - ({""}))
1209 {
1210 if(!(part[1..] in flags)) continue;
1211 if(part[0] == '+')
1212 {
1213 SetProp(P_ALERT,
1214 QueryProp(P_ALERT) | flags[part[1..]]);
1215 }
1216 else if(part[0] == '-')
1217 {
1218 SetProp(P_ALERT,
1219 QueryProp(P_ALERT) & ~ flags[part[1..]]);
1220 }
1221 }
Bugfix895dc452025-06-30 17:35:45 +02001222 // 0 hat leider historisch die Bedeutung von MB_MISC, daher muss hier
1223 // statt dessen auf AL_NO_SOUND gesetzt werden.
1224 if(!QueryProp(P_ALERT))
1225 {
1226 SetProp(P_ALERT, AL_NO_SOUND);
1227 }
Bugfix60f5bc62022-01-21 11:09:56 +01001228 }
MG Mud User88f12472016-06-24 23:31:02 +02001229 }
Zesstra1cc6cf32022-02-09 11:49:31 +01001230 else
1231 {
1232 beep_interval = to_int(str);
1233 if(beep_interval >= 0)
1234 {
1235 SetProp(P_MESSAGE_BEEP, beep_interval);
1236 }
1237 }
MG Mud User88f12472016-06-24 23:31:02 +02001238
Zesstra593b2c72019-11-27 23:37:03 +01001239 beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
Bugfix60f5bc62022-01-21 11:09:56 +01001240 mapping text = ([
1241 MB_SAY: "sage",
1242 MB_TELL: "teile mit",
1243 MB_CHANNEL: "Ebenenmeldungen",
Zesstra3a261e52022-02-10 14:00:31 +01001244 MB_MENTION: "Erwaehnungen",
Zesstra1cc6cf32022-02-09 11:49:31 +01001245 MB_SHOUT: "rufe",
1246 MB_MISC: "sonstige",]);
1247 string types = CountUp(map(filter(m_indices(text), #'_alert_filter_flags), text));
Bugfix60f5bc62022-01-21 11:09:56 +01001248 if(!sizeof(types))
1249 {
Bugfix895dc452025-06-30 17:35:45 +02001250 types = "Keine";
Bugfix60f5bc62022-01-21 11:09:56 +01001251 }
1252 ReceiveNotify(
Bugfix895dc452025-06-30 17:35:45 +02001253 "Gewaehlte Klingeltoene: " + types + "\n"
1254 "Zeitabstand: "
1255 + (beep_interval ? "maximal alle " + beep_interval + " Sekunden." : "kein") + "\n"
Zesstra1cc6cf32022-02-09 11:49:31 +01001256 + (QueryProp(P_ALERT) & AL_NO_SOUND ? " Allerdings sind Toene insgesamt "
1257 "bei Dir abgeschaltet. (\'hilfe ton\')" : ""),
Bugfix60f5bc62022-01-21 11:09:56 +01001258 query_verb());
MG Mud User88f12472016-06-24 23:31:02 +02001259 return 1;
1260}
1261
1262static int _msg_prepend(string str) {
MG Mud User88f12472016-06-24 23:31:02 +02001263 notify_fail("Syntax: senderwiederholung ein/aus\n");
1264 if (stringp(str)) {
1265 if (str=="aus")
1266 SetProp(P_MESSAGE_PREPEND,1);
1267 else if (str=="ein")
1268 SetProp(P_MESSAGE_PREPEND,0);
1269 else return 0;
1270 }
1271
Bugfixb1d9b4d2021-09-14 20:07:04 +02001272 ReceiveNotify("Senderwiederholung bei Mitteilungen: "+
Zesstra04f613c2019-11-27 23:32:54 +01001273 (({int})QueryProp(P_MESSAGE_PREPEND) ? "aus" : "ein")+".",
MG Mud User88f12472016-06-24 23:31:02 +02001274 query_verb());
1275
1276 return 1;
1277}
1278
1279static int _communicate(mixed str, int silent)
1280{
1281 string verb;
1282 string myname;
1283 string msg;
1284
1285 if (!str || extern_call()) str=_unparsed_args()||"";
1286 /* str=_unparsed_args()||""; */
1287 verb = query_verb();
1288 if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
1289 if (str==""||str==" "||!str)
1290 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001291 ReceiveNotify("Was willst Du sagen?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001292 return 1;
1293 }
1294 msg=permutate(str);
1295
1296 myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
1297 !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
1298 "":QueryProp(P_PRESAY))+name())||"";
1299
1300 // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
1301 // ist, keine MT_COMM)
Zesstrabd1236a2022-02-09 22:35:59 +01001302 send_room(environment(), msg, MT_LISTEN|MSG_ALERT, MA_SAY,
MG Mud User88f12472016-06-24 23:31:02 +02001303 capitalize(myname)+" sagt: ", ({this_object()}) );
1304
1305 if(!silent)
1306 {
1307 ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1308 MA_SAY, "Du sagst: ", ME);
1309 }
1310 return 1;
1311}
1312
1313static int _shout_to_all(mixed str)
1314{
1315 string pre, myname, realname, wizards_msg, players_msg;
1316 string wizard_prefix, player_prefix;
1317 int chars;
1318
1319 if (!(str=_unparsed_args()))
1320 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001321 ReceiveNotify("Was willst Du rufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001322 return 1;
1323 }
1324 chars=sizeof(str)/2;
1325 if (chars<4) chars=4;
1326 pre = (!IS_LEARNER(ME) ||
1327 QueryProp(P_INVIS) ||
1328 !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
1329 realname = capitalize((pre + capitalize(getuid()))||"");
1330 myname = capitalize(pre + name()||"");
1331 if (QueryProp(P_INVIS))
1332 realname = "("+realname+")";
1333
1334 wizards_msg = permutate(str);
1335 wizard_prefix = myname+" ruft: ";
1336
1337 if(QueryProp(P_FROG)) {
1338 players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
1339 player_prefix = myname+" quakt: ";
1340 }
1341 else {
1342 players_msg = wizards_msg;
1343 player_prefix = wizard_prefix;
1344 }
1345
1346 if(!IS_LEARNER(this_player()))
1347 {
1348 if(QueryProp(P_GHOST)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001349 ReceiveNotify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
MG Mud User88f12472016-06-24 23:31:02 +02001350 MA_SHOUT);
1351 return 1;
1352 }
1353 if (QueryProp(P_SP) <(chars+20))
1354 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001355 ReceiveNotify("Du musst erst wieder magische Kraefte sammeln.",
MG Mud User88f12472016-06-24 23:31:02 +02001356 MA_SHOUT);
Bugfixb1d9b4d2021-09-14 20:07:04 +02001357 ReceiveNotify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
MG Mud User88f12472016-06-24 23:31:02 +02001358 "Ebenen').", MA_SHOUT);
1359 return 1;
1360 }
1361 SetProp(P_SP, QueryProp(P_SP) - chars - 20);
1362 }
1363
1364 ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
Zesstrabd1236a2022-02-09 22:35:59 +01001365 MA_SHOUT, "Du rufst: ", ME);
MG Mud User88f12472016-06-24 23:31:02 +02001366
1367 foreach ( object ob : users()-({this_object()}) )
1368 if ( IS_LEARNER(ob) )
Zesstrabd1236a2022-02-09 22:35:59 +01001369 ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR|MSG_ALERT, MA_SHOUT,
1370 wizard_prefix, this_object());
MG Mud User88f12472016-06-24 23:31:02 +02001371 else
Zesstrabd1236a2022-02-09 22:35:59 +01001372 ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR|MSG_ALERT, MA_SHOUT,
1373 player_prefix, this_object());
MG Mud User88f12472016-06-24 23:31:02 +02001374
1375 return 1;
1376}
1377
1378varargs int _tell(string who, mixed msg)
1379{
1380 object ob;
1381 string away,myname,ret;
MG Mud User88f12472016-06-24 23:31:02 +02001382 string *xname;
1383 int i,visflag;
1384
1385 if (extern_call() && this_interactive()!=ME) return 1;
1386 if (!who || !msg) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001387 ReceiveNotify("Was willst Du mitteilen?",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001388 return 1;
1389 }
1390
1391 if(who == ERWIDER_PARAM)
1392 {
1393 if (!last_comm_partner)
1394 {
1395 _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
1396 "Du was erwidern\nkoenntest.\n");
1397 return 0;
1398 }
1399 who=last_comm_partner;
1400 }
1401
1402 // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
1403 if (who == ".")
1404 who = ".1";
1405
1406 if ( sscanf(who, ".%d", i) == 1 ) {
1407 if(i > 0 && i <= sizeof(commreceivers))
1408 who = commreceivers[i-1];
1409 else {
1410 _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
1411 return 0;
1412 }
1413 }
1414
1415 xname = explode(who, "@");
1416
Zesstrabd1236a2022-02-09 22:35:59 +01001417 if (sizeof(xname) == 2)
MG Mud User88f12472016-06-24 23:31:02 +02001418 {
1419 if ( QueryProp(P_QP) )
1420 {
Zesstra04f613c2019-11-27 23:32:54 +01001421 if (ret=({string})INETD->_send_udp(xname[1],
MG Mud User88f12472016-06-24 23:31:02 +02001422 ([ REQUEST: "tell",
1423 RECIPIENT: xname[0],
1424 SENDER: getuid(ME),
1425 DATA: msg ]), 1))
1426 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001427 ReceiveNotify(ret, MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001428 }
1429 else
1430 {
1431 write("Nachricht abgeschickt.\n");
1432 add_to_tell_history(who, 1, 0, msg,
Zesstraa31cd5c2016-12-17 20:11:07 +01001433 "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_RTELL, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001434 }
1435 }
1436 else
1437 write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
1438 "Muds etwas mitteilen zu koennen.\n");
1439 return 1;
1440 }
1441
Zesstra8b5320e2022-02-18 21:10:26 +01001442 string|int lname = lower_case(who);
1443 if (!ob=find_player(lname))
MG Mud User88f12472016-06-24 23:31:02 +02001444 {
Zesstra8b5320e2022-02-18 21:10:26 +01001445 lname = match_living(lname, 0);
1446 if (!stringp(lname))
1447 {
1448 switch(lname)
1449 {
MG Mud User88f12472016-06-24 23:31:02 +02001450 case -1:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001451 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001452 return 1;
1453 case -2:
Zesstra8b5320e2022-02-18 21:10:26 +01001454 // check KOBOLD
Zesstra7459f252022-02-23 22:47:26 +01001455 ob = load_object(KOBOLD);
Zesstra8b5320e2022-02-18 21:10:26 +01001456 lname = ({string|int})ob->find_player(lower_case(who));
1457 if (lname == -1) {
1458 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
1459 return 1;
1460 }
1461 else if (!stringp(lname)) {
1462 ReceiveNotify("Kein solcher Spieler (online)!",MA_TELL);
1463 return 1;
1464 }
MG Mud User88f12472016-06-24 23:31:02 +02001465 }
Zesstra8b5320e2022-02-18 21:10:26 +01001466 // jetzt ist ob der KOBOLD und lname die UID eines Spielers.
1467 }
1468 // Wenn wir noch kein ob haben, sollte lname jetzt aber durch das
1469 // match_living() der Name eines Livings sein, dessen Objekt wir noch
1470 // brauchen.
1471 if (!ob)
1472 ob = find_player(lname) || find_living(lname);
MG Mud User88f12472016-06-24 23:31:02 +02001473 if(!ob) {
Zesstra8b5320e2022-02-18 21:10:26 +01001474 ReceiveNotify("Kein solcher Spieler (online)!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001475 return 1;
1476 }
1477 }
1478
Zesstra8b5320e2022-02-18 21:10:26 +01001479 if(QueryProp(P_INVIS))
1480 {
MG Mud User88f12472016-06-24 23:31:02 +02001481 if(!IS_LEARNER(ob))
1482 myname = name();
1483 else
1484 myname="("+
1485 ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
1486 capitalize(getuid()) + ")";
1487 }
1488 else
1489 myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
Bugfix45f88ce2017-03-06 14:41:56 +01001490 QueryProp(P_PRESAY):"") + capitalize(getuid(ME));
Zesstra8b5320e2022-02-18 21:10:26 +01001491 if (myname && sizeof(myname))
1492 myname=capitalize(myname);
1493
1494 // Wir haben Zielobjekt und unseren eigenen Namen. Erstmal an Empfaenger
1495 // senden.
1496 if (living(ob))
1497 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_ALERT, MA_TELL,
1498 myname + " teilt Dir mit: ");
1499 else
1500 _send_to_kobold(lname, permutate(msg), MT_COMM|MT_FAR|MSG_ALERT, MA_TELL,
1501 myname + " teilt Dir mit: ");
MG Mud User88f12472016-06-24 23:31:02 +02001502
1503 // dann evtl. noch an Absender ausgeben...
Zesstra8b5320e2022-02-18 21:10:26 +01001504 visflag = !ob->QueryProp(P_INVIS);
1505 if (visflag || IS_LEARNER(this_player()))
1506 _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(lname) + " mit: ");
MG Mud User88f12472016-06-24 23:31:02 +02001507 // oder irgendwas anderes an den Absender ausgeben...
1508 if (!visflag && interactive(ob))
Zesstra8b5320e2022-02-18 21:10:26 +01001509 ReceiveNotify("Kein solcher Spieler online!",MA_TELL);
Zesstra04f613c2019-11-27 23:32:54 +01001510 else if (away = ({string})ob->QueryProp(P_AWAY))
Zesstra8b5320e2022-02-18 21:10:26 +01001511 ReceiveMsg( break_string( away, 78, capitalize(lname)
MG Mud User88f12472016-06-24 23:31:02 +02001512 + " ist gerade nicht da: ", BS_INDENT_ONCE ),
1513 MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
1514 MA_TELL, 0, this_object());
1515 else if (interactive(ob) && (i=query_idle(ob))>=600)
1516 { //ab 10 Mins
1517 if (i<3600)
1518 away=time2string("%m %M",i);
1519 else
1520 away=time2string("%h %H und %m %M",i);
1521
Bugfixb1d9b4d2021-09-14 20:07:04 +02001522 ReceiveNotify(sprintf("%s ist seit %s voellig untaetig.",
Zesstra8b5320e2022-02-18 21:10:26 +01001523 capitalize(lname),away),
MG Mud User88f12472016-06-24 23:31:02 +02001524 MA_TELL);
1525 }
1526
1527 return 1;
1528}
1529
1530static int _teile(string str)
1531{
1532 string who, message;
1533 if (!(str=_unparsed_args())) return 0;
1534 if (sscanf(str, "%s mit %s", who, message) == 2)
1535 return _tell(who, message,1);
1536 return 0;
1537}
1538static int _teile_mit_alias(string str)
1539{
1540 str = _unparsed_args(), TRIM_LEFT;
1541 if (!str) return 0;
1542 str = trim(str, TRIM_LEFT);
1543 // Ziel muss min. 2 Buchstaben haben (.<nr>)
1544 if (sizeof(str) < 4) return 0;
1545 int pos = strstr(str, " ");
1546 if (pos >= 2)
1547 return _tell(str[..pos-1], str[pos+1..]);
1548 return 0;
1549}
1550
1551static int _erzaehle(string str)
1552{
1553 string who, message;
1554
1555 if (!(str=_unparsed_args())) return 0;
1556 if (sscanf(str, "%s %s", who, message) == 2)
1557 return _tell(who, message,1);
1558 return 0;
1559}
1560
1561static int _whisper(string str)
1562{
1563 object ob;
1564 string who;
1565 string msg;
1566 string myname;
1567
1568 if (!(str=_unparsed_args()) ||
1569 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1570 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001571 ReceiveNotify("Was willst Du wem zufluestern?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001572 return 1;
1573 }
Zesstra6e88b6a2019-11-08 00:25:39 +01001574 who = lower_case(who);
MG Mud User88f12472016-06-24 23:31:02 +02001575 if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001576 ReceiveNotify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001577 return 1;
1578 }
1579
1580 myname = capitalize((((IS_LEARNER(ME) &&
1581 !QueryProp(P_INVIS) &&
1582 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1583 QueryProp(P_PRESAY) : "") + name()) || "");
1584
1585 _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
Bugfixdbdbed52022-01-21 11:14:35 +01001586 MA_SAY, myname + " fluestert Dir zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001587 send_room(environment(),
1588 myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
1589 MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
1590
1591 _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
1592
1593
1594 return 1;
1595}
1596
1597static int _remote_whisper(string str)
1598{
1599 /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
1600 Nachricht nicht in der tell_history verewigt. */
1601
1602 object ob;
Zesstra8b5320e2022-02-18 21:10:26 +01001603 string who;
MG Mud User88f12472016-06-24 23:31:02 +02001604 string msg;
1605 string myname;
1606
1607 if (!(str=_unparsed_args()) ||
1608 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1609 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001610 ReceiveNotify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001611 return 1;
1612 }
1613
Zesstra8b5320e2022-02-18 21:10:26 +01001614 string|int lname = lower_case(who);
1615 if (!ob=find_player(lname))
MG Mud User88f12472016-06-24 23:31:02 +02001616 {
Zesstra8b5320e2022-02-18 21:10:26 +01001617 lname = match_living(lname, 0);
1618 if (!stringp(lname))
1619 {
1620 switch(lname)
1621 {
MG Mud User88f12472016-06-24 23:31:02 +02001622 case -1:
Zesstra8b5320e2022-02-18 21:10:26 +01001623 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001624 return 1;
1625 case -2:
Zesstra8b5320e2022-02-18 21:10:26 +01001626 // rfluester benutzt keinen KOBOLD.
1627 ReceiveNotify("Kein solcher Spieler!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001628 return 1;
1629 }
Zesstra8b5320e2022-02-18 21:10:26 +01001630 }
1631 // lname sollte nun eindeutig sein.
1632 ob = find_player(lname) || find_living(lname);
1633 if(!ob) {
1634 ReceiveNotify("Kein solcher Spieler online!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001635 return 1;
1636 }
1637 }
Zesstra8b5320e2022-02-18 21:10:26 +01001638
MG Mud User88f12472016-06-24 23:31:02 +02001639 if (environment(ob) == environment()) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001640 ReceiveNotify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001641 return 1;
1642 }
1643
1644 myname = capitalize((((IS_LEARNER(ME) &&
1645 !QueryProp(P_INVIS) &&
1646 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1647 QueryProp(P_PRESAY) : "") + name()) || "");
1648
1649 // An Empfaenger senden.
Zesstrabd1236a2022-02-09 22:35:59 +01001650 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE,
1651 MA_EMOTE, myname + " fluestert Dir aus der Ferne zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001652 // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
1653 if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
Zesstra8b5320e2022-02-18 21:10:26 +01001654 ReceiveNotify("Kein solcher Spieler online!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001655 return 1;
1656 }
1657 // sonst eigene Meldung via _recv() ausgeben.
1658 else
1659 _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
1660 "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
1661
1662 return 1;
1663}
1664
1665static int _converse(string arg)
1666{
Bugfixb1d9b4d2021-09-14 20:07:04 +02001667 ReceiveNotify("Mit '**' wird das Gespraech beendet.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001668 if (stringp(arg) && strstr(arg, "-s") == 0)
1669 input_to("_converse_more", INPUT_PROMPT, "]", 1);
1670 else
1671 input_to("_converse_more", INPUT_PROMPT, "]", 0);
1672 return 1;
1673}
1674
1675static int _converse_more(mixed str, int silent)
1676{
1677 if (str == "**") {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001678 ReceiveNotify("Ok.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001679 return 0;
1680 }
1681
1682 if(str != "")
1683 _communicate(str, silent);
1684
1685 input_to("_converse_more", INPUT_PROMPT, "]", silent);
1686 return 1;
1687}
1688
1689private int is_learner(object o) { return IS_LEARNER(o); }
1690
1691static int _shout_to_wizards(mixed str)
1692{
MG Mud User88f12472016-06-24 23:31:02 +02001693 string myname;
MG Mud User88f12472016-06-24 23:31:02 +02001694
1695 str = _unparsed_args();
1696 if (!str||!sizeof(str)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001697 ReceiveNotify("Was willst Du den Magiern zurufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001698 return 1;
1699 }
1700 // Kontrollzeichen rausfiltern.
1701 str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
1702 myname = capitalize(getuid(this_object()));
1703 if (!IS_LEARNER(this_object()))
1704 _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
1705
1706 // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
1707 filter(users(), #'is_learner)->ReceiveMsg(str,
Zesstrabd1236a2022-02-09 22:35:59 +01001708 MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_ALERT,
MG Mud User88f12472016-06-24 23:31:02 +02001709 MA_SHOUT, myname+" an alle Magier: ", this_object());
1710
1711 return 1;
1712}
1713
1714static int _echo(string str) {
1715 if (!IS_SEER(ME) || (!IS_LEARNER(ME)
1716 && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
1717 return 0;
1718
1719 if (!(str=_unparsed_args())) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001720 ReceiveNotify("Was moechtest Du 'echoen'?", 0);
MG Mud User88f12472016-06-24 23:31:02 +02001721 return 1;
1722 }
1723
1724 if (!IS_LEARNER(this_interactive()))
1725 {
1726 if (QueryProp(P_GHOST))
1727 {
1728 _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
1729 return 0;
1730 }
1731 if (QueryProp(P_SP)<ECHO_COST)
1732 {
1733 _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
1734 return 0;
1735 }
1736 SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
1737 str=">\b"+str;
1738 log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
1739 str));
1740 }
1741 // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
1742 // Kommhistory noch im Kobold landen.
1743 send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
1744 MA_UNKNOWN, 0, 0);
1745 return 1;
1746}
1747
1748// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
1749// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
1750// und gefedert.
1751static string *_set_ignore(mixed arg)
1752{
1753 raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
1754 "Benutze AddIgnore/RemoveIgnore()!\n");
1755}
1756// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
1757// nix dran, dass alle TestIgnore() & Co benutzen sollen.
1758static string *_query_ignore() {
1759 mixed ign=Query(P_IGNORE, F_VALUE);
1760 if (mappingp(ign))
1761 return m_indices(ign);
1762 return ({});
1763}
1764
1765public int AddIgnore(string ign) {
1766 // Einige strings sind nicht erlaubt, z.B. konsekutive .
1767 if (!sizeof(ign)
1768 || regmatch(ign,"[.]{2,}",RE_PCRE)
1769 || regmatch(ign," ",RE_PCRE)
1770 || sizeof(explode(ign,"."))>3)
1771 return 0;
1772
1773 mapping ignores=Query(P_IGNORE, F_VALUE);
1774 ignores[ign]=time();
1775 // kein Set() noetig.
1776 return 1;
1777}
1778
1779public int RemoveIgnore(string ign)
1780{
1781 mapping ignores=Query(P_IGNORE,F_VALUE);
1782 m_delete(ignores,ign);
1783 // Kein Set() noetig
1784 return 1;
1785}
1786
1787static int _query_intermud()
1788{
1789 mixed tmp;
1790 return member(pointerp(tmp=Query(P_CHANNELS))?tmp:({}), "Intermud") > -1;
1791}
1792
1793
1794int erwidere(string str)
1795{
1796 str=_unparsed_args();
1797 if (!str) return 0;
1798 return _tell(ERWIDER_PARAM, str ,1);
1799}
1800
1801static int tmhist(string str)
1802{
1803
1804 if (str == "aus") {
1805 tell_history_enabled = TELLHIST_DISABLED;
1806 write("Ok, es wird nichts mehr gespeichert.\n");
1807 if (sizeof(tell_history)) {
Zesstra996bafe2022-02-14 22:42:39 +01001808 clear_tell_history(1);
MG Mud User88f12472016-06-24 23:31:02 +02001809 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1810 }
1811 return 1;
1812 }
Zesstra996bafe2022-02-14 22:42:39 +01001813 if (str == "loeschen")
1814 {
1815 if (sizeof(tell_history)) {
1816 clear_tell_history(1);
1817 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1818 }
1819 else
1820 write("Deine Mitteilungsgeschichte war schon leer.\n");
1821 return 1;
1822 }
MG Mud User88f12472016-06-24 23:31:02 +02001823
1824 if (str == "namen") {
1825 int flag;
1826 tell_history_enabled = TELLHIST_NO_MESSAGE;
1827 write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
1828 foreach (string uid, struct chat_s chat: tell_history)
1829 if (pointerp(chat->msgbuf)) {
1830 chat->msgbuf = 0;
1831 chat->ptr = 0;
1832 flag = 1;
1833 }
1834 if (flag)
1835 write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
1836 return 1;
1837 }
1838
1839 if (str == "ein" || str == "an") {
1840 tell_history_enabled = TELLHIST_ENABLED;
1841 write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
1842 return 1;
1843 }
1844
MG Mud User88f12472016-06-24 23:31:02 +02001845 if (str == "langlebig") {
1846 tell_history_enabled = TELLHIST_LONGLIFE;
1847 write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
1848 "Ende/Crash/\nReboot gespeichert.\n");
1849 return 1;
1850 }
MG Mud User88f12472016-06-24 23:31:02 +02001851
1852 if (str == "status") {
1853 switch (tell_history_enabled) {
1854 case TELLHIST_DISABLED:
1855 write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
1856 break;
1857 case TELLHIST_NO_MESSAGE:
1858 write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
1859 break;
1860 case TELLHIST_ENABLED:
1861 write("Deine Mitteilungen werden gespeichert.\n");
1862 break;
MG Mud User88f12472016-06-24 23:31:02 +02001863 case TELLHIST_LONGLIFE:
1864 write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
1865 "Crash/Reboot\ngespeichert.\n");
1866 break;
MG Mud User88f12472016-06-24 23:31:02 +02001867 }
1868 return 1;
1869 }
1870
1871 if (tell_history_enabled == TELLHIST_DISABLED) {
1872 _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
1873 return 0;
1874 }
1875
1876 if (!sizeof(tell_history)) {
1877 _notify_fail("Du hast noch keinem etwas mitgeteilt "
1878 "und noch keine Mitteilungen erhalten.\n");
1879 return 0;
1880 }
1881
1882 if (str && sizeof(str)) {
1883
1884 if (tell_history_enabled < TELLHIST_ENABLED) {
1885 _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
1886 return 0;
1887 }
1888
1889 string uid;
1890 if (member(tell_history, str)) {
1891 // Name gewuenscht, da der String in der History vorkommt.
1892 uid = str;
1893 }
1894 else {
1895 // evtl. ne Zahl angegeben.
1896 int i;
1897 string *partners = sorted_commpartners(0);
1898 if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
1899 uid = partners[i];
1900 else {
1901 notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
1902 return 0;
1903 }
1904 }
1905
1906 mixed *data = tell_history[uid]->msgbuf;
1907 if (!data) {
1908 _notify_fail(
1909 "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
1910 return 0;
1911 }
1912
1913 int ptr = tell_history[uid]->ptr;
1914
1915 More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
1916 data[0..ptr-1],
Zesstra7459f252022-02-23 22:47:26 +01001917 function string (struct kobold_msg_s msg) {
MG Mud User88f12472016-06-24 23:31:02 +02001918 if (!structp(msg)) return "";
Zesstra3a261e52022-02-10 14:00:31 +01001919 return break_string(terminal_colour(msg->msg, colourmap)
Zesstra7459f252022-02-23 22:47:26 +01001920 + " ["
1921 + strftime("%H:%M:%S",msg->timestamp) + "]", 78,
MG Mud User88f12472016-06-24 23:31:02 +02001922 msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
1923 } ) ) );
1924 return 1;
1925 }
1926
1927 string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
1928 int i;
1929 foreach (string uid : sorted_commpartners(0) ) {
1930 int j;
1931 struct chat_s chat = tell_history[uid];
1932 history += sprintf("%2d.%-4s %s %-11s %d gesendet/%d empfangen\n", ++i,
1933 ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
1934 strftime("%a, %e.%m.%y",chat->time_last_msg),
1935 capitalize(chat->uid), chat->sentcount, chat->recvcount);
1936 }
1937
1938 More(history);
1939
1940 return 1;
1941}
1942
1943static mixed _query_localcmds()
1944{
1945 return ({
1946 ({"kobold", "cmd_kobold",0,0}),
1947 ({"sag","_communicate",0,0}),
1948 ({"sage","_communicate",0,0}),
1949 ({"'","_communicate",1,0}),
1950 ({"mruf","_shout_to_wizards",0,0}),
1951 ({"mrufe","_shout_to_wizards",0,0}),
1952 ({"ruf","_shout_to_all",0,0}),
1953 ({"rufe","_shout_to_all",0,0}),
1954 ({"erzaehl","_erzaehle",0,0}),
1955 ({"erzaehle","_erzaehle",0,0}),
1956 ({"teil","_teile",0,0}),
1957 ({"teile","_teile",0,0}),
1958 ({"tm","_teile_mit_alias",0,0}),
1959 ({"fluester","_whisper",0,0}),
1960 ({"fluestere","_whisper",0,0}),
1961 ({"rfluester","_remote_whisper",0,0}),
1962 ({"rfluestere","_remote_whisper",0,0}),
1963 ({"gespraech","_converse",0,0}),
1964 ({"echo","_echo",0,0}),
1965 ({"ignorier","ignoriere",0,0}),
1966 ({"ignoriere","ignoriere",0,0}),
1967 ({"tmhist","tmhist",0,0}),
1968 ({"erwider","erwidere",0,0}),
1969 ({"erwidere","erwidere",0,0}),
1970 ({"klingelton","_msg_beep",0,0}),
1971 ({"senderwiederholung","_msg_prepend",0,0}),
1972 ({"report","set_report",0,0}),
1973 })+channel::_query_localcmds();
1974}
1975
1976private string *sorted_commpartners(int reversed) {
1977 return sort_array(m_indices(tell_history),
1978 function int (string uid1, string uid2) {
1979 if (reversed)
1980 return tell_history[uid1]->time_last_msg >
1981 tell_history[uid2]->time_last_msg;
1982 else
1983 return tell_history[uid1]->time_last_msg <=
1984 tell_history[uid2]->time_last_msg;
1985 } );
1986}
1987
Zesstra0712bac2020-06-12 09:41:24 +02001988// Setzt den Prompt eines Interactives. Gerufen bei Objekterstellung,
1989// Reconnect und bei Magiern, wenn diese ihren Prompt oder ihr aktuelles
1990// Verzeichnis aendern.
MG Mud User88f12472016-06-24 23:31:02 +02001991static void modify_prompt() {
1992 string text = Query(P_PROMPT, F_VALUE);
1993
1994 if ( !stringp(text) || !sizeof(text) )
1995 text = "> ";
1996 else {
1997 string path = Query(P_CURRENTDIR, F_VALUE);
1998 if (stringp(path) && sizeof(path))
1999 text = regreplace(text,"\\w",path,0); // Pfad einsetzen
2000 }
2001 configure_interactive(this_object(), IC_PROMPT, text);
2002}
2003
2004// Prueft auf Ingoriereeintraege.
2005// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
MG Mud User88f12472016-06-24 23:31:02 +02002006public int TestIgnore(string|string* srcnames)
MG Mud User88f12472016-06-24 23:31:02 +02002007{
2008 mapping ign = Query(P_IGNORE, F_VALUE);
2009 if (stringp(srcnames))
2010 srcnames = ({srcnames});
2011
2012 foreach(string srcname: srcnames)
2013 {
2014 // einfachster Fall, exakter Match
2015 if (member(ign, srcname))
2016 return MSG_IGNORED;
2017 // ansonsten muss aufgetrennt werden.
2018 if (strstr(srcname,".") > -1)
2019 {
2020 string *srcparts=explode(srcname,".");
2021 switch(sizeof(srcparts))
2022 {
2023 // case 0 und 1 kann nicht passieren.
2024 case 3:
2025 // zu pruefen: [sender].aktion.qualifizierer.
2026 // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
2027 // oben schon geprueft. im Spieler geprueft werden muss noch:
2028 // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
2029 if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
2030 || member(ign, "."+srcparts[1]) // .aktion
2031 || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
2032 || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
2033 )
2034 {
2035 return MSG_IGNORED;
2036 }
2037 break;
2038 case 2:
2039 // zu pruefen: spieler.aktion
2040 // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
2041 // oben schon geprueft. Im Spieler zu pruefen ist noch:
2042 // spieler und .aktion
2043 if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
2044 || member(ign, "."+srcparts[1]))
2045 {
2046 return MSG_IGNORED;
2047 }
2048 break;
2049 default: // mehr als 3 Teile...
2050 raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
2051 "is supported. Got: %s\n",srcname));
MG Mud User88f12472016-06-24 23:31:02 +02002052 }
2053 }
2054 }
2055 // Default: nicht ignorieren.
2056 return 0;
2057}
2058
2059#ifdef __LPC_UNIONS__
2060public int TestIgnoreExt(string|string* srcnames)
2061#else
2062public int TestIgnoreExt(mixed srcnames)
2063#endif
2064{
2065 return TestIgnore(srcnames);
2066}
2067
2068// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
2069// Typen.
2070// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
2071private int check_ignores(string msg, int msg_type, string msg_action,
Zesstra7459f252022-02-23 22:47:26 +01002072 string msg_prefix, object|string origin)
MG Mud User88f12472016-06-24 23:31:02 +02002073{
2074 // Einige Dinge lassen sich nicht ignorieren.
2075 if (msg_type & (MT_NEWS|MT_NOTIFICATION))
2076 return 0;
2077 // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
2078 // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
2079 // eine ignorierbare msg_action geben.
2080 else if (stringp(msg_action) && origin && origin != ME)
2081 {
Zesstra7459f252022-02-23 22:47:26 +01002082 string srcname;
2083 if (objectp(origin))
2084 srcname =
MG Mud User88f12472016-06-24 23:31:02 +02002085 (query_once_interactive(origin) ? origin->query_real_name()
2086 : origin->name(WER) || "");
Zesstra7459f252022-02-23 22:47:26 +01002087 else
2088 srcname = origin;
2089
MG Mud User88f12472016-06-24 23:31:02 +02002090 mapping ign = Query(P_IGNORE, F_VALUE);
2091
2092 if (member(ign, srcname))
2093 return MSG_IGNORED;
2094 // vielleicht wird irgendwas a la name.aktion ignoriert?
2095 // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
2096 // Ebenenmeldungen)
2097 if (member(ign, srcname+"."+msg_action))
2098 return MSG_VERB_IGN;
2099 // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
2100 // das reichlich sinnfrei ist.
2101 if (member(ign, "."+msg_action))
2102 return MSG_VERB_IGN;
2103 // Spieler auf Ebenen ignoriert?
2104 // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
2105 if (strstr(msg_action, MA_CHANNEL) == 0)
2106 {
2107 // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
2108 if (member(ign, srcname + "."MA_CHANNEL))
2109 return MSG_IGNORED;
2110 // spieler.ebenen.ebenenname ist oben schon abgedeckt.
2111 // .ebenen halte ich fuer sinnfrei, nicht geprueft.
2112 }
2113 // Spieler aus anderem mud? *seufz*
2114 if (strstr(srcname,"@") > -1)
2115 {
2116 string *srcparts = explode(srcname,"@");
2117 if (sizeof(srcparts)==2)
2118 {
2119 // spieler@?
2120 if (member(ign, srcparts[0]+"@"))
2121 return MSG_IGNORED;
2122 // oder Mud per @mud?
2123 if (member(ign, "@" + srcparts[1]))
2124 return MSG_MUD_IGN;
2125 // BTW: spieler@mud wurde schon ganz oben erfasst.
2126 }
2127 }
2128 }
2129 // Default: nicht ignorieren.
2130 return 0;
2131}
2132
2133// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
2134// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
Bugfixe0fc68f2022-01-07 15:30:54 +01002135// BLindheit/Taubheit). In Zukunft koennten aber weitere Typen und weitere
2136// Kriterien wichtig werden und weitere Argumente uebergeben werden. (Dies
2137// bitte beachten, falls diese Funktion protected/public werden sollte.)
MG Mud User88f12472016-06-24 23:31:02 +02002138// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
2139// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
2140// Sinn gar nix zu tun hat.
2141// Rueckgabe: 0 oder MSG_SENSE_BLOCK
Bugfixe0fc68f2022-01-07 15:30:54 +01002142private int check_senses(int msg_type)
MG Mud User88f12472016-06-24 23:31:02 +02002143{
2144 int senses = msg_type & (MT_LOOK|MT_LISTEN);
2145 // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
2146 // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
2147 // unterdrueckt werden.
2148 if (!senses)
2149 return 0;
Bugfix427a5812022-01-07 15:41:24 +01002150
2151 int orig_senses = senses;
2152
MG Mud User88f12472016-06-24 23:31:02 +02002153 if ((senses & MT_LOOK) && CannotSee(1))
2154 senses &= ~MT_LOOK; // Sinn loeschen
2155
2156 if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
2157 senses &= ~MT_LISTEN;
2158
Bugfix427a5812022-01-07 15:41:24 +01002159 // Wenn kein Sinn mehr ueber ist oder all_types gesetzt ist und nicht mehr
2160 // alle Sinne vorhanden sind, wird die Nachricht nicht wahrgenommen.
2161 if (orig_senses == senses // kein Sinn geloescht, haeufigster Fall
2162 || (!(msg_type&MSG_ALL_SENSES) && senses) ) // min. ein Sinn uebrig
2163 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02002164
Bugfix427a5812022-01-07 15:41:24 +01002165 return MSG_SENSE_BLOCK;
MG Mud User88f12472016-06-24 23:31:02 +02002166}
2167
Zesstra09bcf042023-12-30 20:07:19 +01002168public int ReceiveMsg(string msg, int msg_type, string msg_action,
MG Mud User88f12472016-06-24 23:31:02 +02002169 string msg_prefix, object origin)
2170{
2171 if (!msg) return MSG_FAILED;
2172
2173 // Flags und Typen spalten
2174 int flags = msg_type & MSG_ALL_FLAGS;
2175 int type = msg_type & ~flags;
2176
Zesstra09bcf042023-12-30 20:07:19 +01002177 // msg_action kommt per Defaultargument, msg_type hat per Default 0, daher
2178 // ggf. auch noch den Typ raten (leider geht msg_type nicht als
2179 // Defaultargument, da es *nach* msg_action ausgewertet werden muss, aber in
2180 // der Argumentliste leider *vor* msg_action kommt...
MG Mud User88f12472016-06-24 23:31:02 +02002181 type ||= comm_guess_message_type(msg_action, origin);
2182
2183 // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
2184 if (msg_type & MT_DEBUG)
2185 {
2186 if (!QueryProp(P_WIZ_DEBUG)
2187 || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
2188 return MSG_FAILED;
2189 }
2190
2191 // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
2192 // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
2193 // Weiterbearbeitung.
2194 // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
2195 if (!(flags & MSG_DONT_IGNORE))
2196 {
Bugfix427a5812022-01-07 15:41:24 +01002197 // Sinne pruefen.
2198 int res=check_senses(msg_type);
MG Mud User88f12472016-06-24 23:31:02 +02002199 if (res) return res;
2200
2201 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
2202 res=check_ignores(msg, type, msg_action, msg_prefix, origin);
2203 if (res) return res;
2204 }
2205
Zesstra3a261e52022-02-10 14:00:31 +01002206 // Mentions in der Form @Charname werden in Kommunikation oder beim Hoeren
2207 // von Rufen und Sagen markiert und gegebenfalls gibt es ein Pieps dafuer
2208 int mention;
2209 if ((type & MT_COMM)
2210 || ((type & MT_LISTEN) && msg_action in ({MA_SAY, MA_SHOUT}))
2211 )
2212 {
2213 // Um den Charnamen Tags fuer terminal_colour() einfuegen.
2214 // Lookahead und Lookbehind assertions um die Whitespaces um das Wort
2215 // nicht in den Match einzuschliessen (und zu ersetzen).
2216 string tmp = regreplace(msg,
Zesstra88cf8172025-06-28 13:19:53 +02002217 sprintf("(?<=\\s){0,1}@%s(?=\\s*){0,1}",getuid(ME)),
Zesstra3a261e52022-02-10 14:00:31 +01002218 sprintf("%%^mention%%^@%s%%^normal%%^",
2219 capitalize(getuid(ME))),
2220 RE_PCRE|RE_GLOBAL|RE_CASELESS);
2221 send_debug(ME, tmp);
2222 // Der Vergleich ist weniger schlimm als es aussieht, weil die Strings
2223 // unterschiedlich gross sein werden und daher nicht zeichenweise
2224 // verglichen werden muessen. Es ist dann jedenfalls schneller als
2225 // getrennt mit regmatch oder strstr zu schauen, ob @charname
2226 // vorkommt.
2227 if (tmp != msg)
2228 {
2229 msg = tmp;
2230 mention = 1;
2231 }
2232 }
2233
MG Mud User88f12472016-06-24 23:31:02 +02002234 // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
2235 if ((type & MT_COMM))
2236 {
2237 // erstmal in der Komm-History ablegen, wenn gewuenscht.
2238 if ((!(flags & MSG_DONT_STORE)))
2239 {
2240 string uid;
2241 if (query_once_interactive(origin))
2242 uid = origin->query_real_name();
2243 else
2244 uid = origin->name(WER) || "<unbekannt>";
Zesstraa31cd5c2016-12-17 20:11:07 +01002245 add_to_tell_history(uid, 0, 1, msg, msg_prefix,
2246 (msg_action == MA_TELL ? MSGFLAG_TELL : 0 ) );
MG Mud User88f12472016-06-24 23:31:02 +02002247 }
2248
2249 // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
2250 // Ebenenmeldungen. (Die haben ggf. schon.)
2251 if (stringp(msg_action) && QueryProp(P_AWAY)
2252 && strstr(msg_action, MA_CHANNEL) != 0)
2253 {
2254 // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
2255 if (msg[<1] == '\n')
2256 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
2257 else
2258 msg = msg + " [" + strftime("%H:%M",time()) + "]";
2259 }
2260 // Kobold erlaubt und gewuenscht? Kobold ist fuer die
2261 // direkte Kommunikation mittels MT_COMM vorgesehen.
2262 // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
2263 // ignorieren.
2264 // die if-Konstruktion ist so, weil ich das _flush_cache() im else
2265 // brauche.
2266 if (query_editing(this_object()) || query_input_pending(this_object())
2267 || QueryProp(P_EARMUFFS))
2268 {
2269 if (!(flags & MSG_DONT_BUFFER)
Zesstra7459f252022-02-23 22:47:26 +01002270 && (QueryProp(P_BUFFER) & KOBOLD_ONLINE))
MG Mud User88f12472016-06-24 23:31:02 +02002271 {
2272 // Nachricht soll im Kobold gespeichert werden.
2273 return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
2274 }
2275 }
2276 else
2277 {
2278 // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
2279 // entleeren.
2280 _flush_cache(0);
2281 }
Bugfix3afcb792022-01-21 22:32:42 +01002282 }
2283
Zesstra3a261e52022-02-10 14:00:31 +01002284 // Farbtags ersetzen
2285 msg = terminal_colour(msg, colourmap);
2286
2287 // Alertton senden?
2288 if ((msg_type & MSG_ALERT) || mention)
2289 comm_beep(msg_action, mention);
MG Mud User88f12472016-06-24 23:31:02 +02002290
2291 // Ausgabenachricht bauen und an den Spieler senden.
2292 if (flags & MSG_DONT_WRAP)
2293 msg = (msg_prefix ? msg_prefix : "") + msg;
2294 else
2295 {
2296 int bsflags = flags & MSG_ALL_BS_FLAGS;
2297 if (QueryProp(P_MESSAGE_PREPEND))
2298 bsflags |= BS_PREPEND_INDENT;
2299 msg = break_string(msg, 78, msg_prefix, bsflags);
2300 }
2301 efun::tell_object(ME, msg);
2302
2303 return MSG_DELIVERED;
2304}
Zesstra7459f252022-02-23 22:47:26 +01002305
2306// called from base.c in Reconnect()
2307protected void reconnect() {
2308 // Cache fuer den report zuruecksetzen, der koennte veraltet sein (insb.
2309 // falls in der letzten Session GMCP benutzt wurde und jetzt nicht).
2310 report_cache = 0;
2311}
2312
2313protected void updates_after_restore(int newflag) {
2314 // Colourmap aktualisieren nach Restore
2315 colourmap = build_colourmap(QueryProp(P_TTY));
2316
2317 // Altes Ignoriere loeschen...
2318 mixed ign = Query(P_IGNORE,F_VALUE);
2319 if (!mappingp(ign))
2320 {
2321 if (pointerp(ign))
2322 ReceiveNotify(break_string(
2323 "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
2324 "weil es eine Aktualisierung der Ignorierefunktion gab, "
2325 "bei der eine Konversion der Daten leider nicht "
2326 "moeglich war.",78), 0);
2327
2328 Set(P_IGNORE, ([]), F_VALUE);
2329 }
2330 // ggf. Comm-Vault abrufen oder neu erstellen.
2331 setup_comm_vault();
2332 // Wenn es eins gibt, den Inhalt zu unserem internen Koboldpuffer
2333 // hinzufuegen, von wo es spaeter angezeigt wird.
2334 if (commvault)
2335 process_comm_vault(commvault);
Bugfix895dc452025-06-30 17:35:45 +02002336
Zesstra47bff292025-07-08 21:40:05 +02002337 // P_ALERT korrigieren. Das historische Originalverhalten war, bei P_ALERT
2338 // == 0 alle Klingeltoene abzuspielen. Default fuer heute soll die gezielte
2339 // Kommunikation an den Spieler einschliessen.
Bugfix895dc452025-06-30 17:35:45 +02002340 if(!QueryProp(P_ALERT))
2341 {
Zesstra47bff292025-07-08 21:40:05 +02002342 SetProp(P_ALERT, MB_TELL|MB_MENTION|MB_MISC);
Bugfix895dc452025-06-30 17:35:45 +02002343 }
Zesstra7459f252022-02-23 22:47:26 +01002344}
2345