blob: 95c1a7b1f25331980d1a531e72f34351f6d37f24 [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);
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);
105 Set(P_INTERMUD, SAVE, F_MODE);
106 Set(P_IGNORE, ([]), F_VALUE);
107 Set(P_IGNORE, SAVE, F_MODE);
108 Set(P_BUFFER, SAVE, F_MODE);
Zesstra7459f252022-02-23 22:47:26 +0100109 SetProp(P_BUFFER, KOBOLD_OFFLINE);
MG Mud User88f12472016-06-24 23:31:02 +0200110 Set(P_MESSAGE_PREPEND, SAVE, F_MODE_AS);
111 Set(P_MESSAGE_BEEP, SAVE, F_MODE_AS);
112}
113
114void create_super()
115{
116 set_next_reset(-1);
117}
118
Zesstra3a261e52022-02-10 14:00:31 +0100119private mapping build_colourmap(string ttype)
120{
121 mapping res = ([0:""]);
122 switch(ttype)
123 {
124 case "dumb":
125 return res;
126 case "ansi":
127 res = ([ 0:ANSI_NORMAL, "normal": ANSI_NORMAL,
128 "bold": ANSI_BOLD, "underlined": ANSI_UNDERL,
129 "blink": ANSI_BLINK, "invers": ANSI_INVERS,
130 "black": ANSI_BLACK, "red": ANSI_RED,
131 "green": ANSI_GREEN, "yellow": ANSI_YELLOW,
132 "blue": ANSI_BLUE, "purple": ANSI_PURPLE,
133 "cyan": ANSI_CYAN, "white": ANSI_WHITE,
134 "bg_black": ANSI_BG_BLACK, "bg_red": ANSI_BG_RED,
135 "bg_green": ANSI_BG_GREEN, "bg_yellow": ANSI_BG_YELLOW,
136 "bg_blue": ANSI_BG_BLUE, "bg_purple": ANSI_BG_PURPLE,
137 "bg_cyan": ANSI_BG_CYAN, "bg_white": ANSI_BG_WHITE,
138 "mention": ANSI_BOLD+ANSI_BG_BLUE,
139 ]);
140 break;
141 case "vt100":
142 res += ([0:ANSI_NORMAL, "normal": ANSI_NORMAL,
143 "bold": ANSI_BOLD, "underlined": ANSI_UNDERL,
144 "blink": ANSI_BLINK, "invers": ANSI_INVERS,
145 "mention": ANSI_BOLD,
146 ]);
147 break;
148 default:
149 assert(1, "Ungueltiger Terminaltyp in build_colourmap");
150 }
151 return res;
152}
153
154protected void set_colourmap(string ttype="ansi")
155{
156 colourmap = build_colourmap(ttype);
157}
158
Zesstra7459f252022-02-23 22:47:26 +0100159private void setup_comm_vault()
160{
161 if (!commvault && (QueryProp(P_BUFFER) & KOBOLD_OFFLINE))
MG Mud User88f12472016-06-24 23:31:02 +0200162 {
Zesstra7459f252022-02-23 22:47:26 +0100163 // Schauen, ob ein Vault im KOBOLD existiert.
164 commvault = KOBOLD->RetrieveVault();
165 // Wenn nicht, aber eins gewuenscht ist, wird eins erstellt und in KOBOLD
166 // hinterlegt.
167 if (!commvault)
168 {
169 commvault = new_lwobject("/std/player/comm_vault");
170 KOBOLD->DepositVault(commvault);
171 }
MG Mud User88f12472016-06-24 23:31:02 +0200172 }
173}
174
Zesstra2504b2d2020-05-22 12:30:17 +0200175static int set_report(string str)
176{
MG Mud User88f12472016-06-24 23:31:02 +0200177 int canflags = QueryProp(P_CAN_FLAGS);
MG Mud User88f12472016-06-24 23:31:02 +0200178 if(!str)
179 {
Zesstracf8f2952020-05-22 12:07:52 +0200180 if (stat_reports)
181 {
182 string *res=({});
183 if (stat_reports & DO_REPORT_HP)
184 res+=({"Lebenspunkte"});
185 if (stat_reports & DO_REPORT_SP)
186 res+=({"Konzentrationspunkte"});
187 if (stat_reports & DO_REPORT_POISON)
188 res+=({"Vergiftungen"});
189 if (stat_reports & DO_REPORT_WIMPY)
190 res+=({"Vorsicht"});
MG Mud User88f12472016-06-24 23:31:02 +0200191
Zesstracf8f2952020-05-22 12:07:52 +0200192 tell_object(ME,break_string(
MG Mud User88f12472016-06-24 23:31:02 +0200193 "Dir werden jetzt Veraenderungen Deiner "
194 +CountUp(res) + " berichtet.",78));
Zesstra86ec63b2020-05-22 12:09:56 +0200195 if (GMCP_Status("MG.char") || GMCP_Status("char")
196 || GMCP_Status("Char"))
197 {
198 tell_object(ME,break_string(
199 "Achtung: Dein Client laesst sich den Report per GMCP "
Zesstra2504b2d2020-05-22 12:30:17 +0200200 "(s. 'hilfe GMCP') uebermitteln. Daher wird er Dir nicht "
Zesstra86ec63b2020-05-22 12:09:56 +0200201 "in der Textausgabe des Spiels angezeigt! Moechtest Du "
202 "dies nicht, schalte bitte in Deinem Client GMCP-Module mit "
203 "Namen wie 'MG.char', 'char', 'Char' oder aehnliche aus."));
204 }
MG Mud User88f12472016-06-24 23:31:02 +0200205 }
206 else
207 tell_object(ME,
208 "Alle Statusreports sind ausgeschaltet.\n");
209
210 return 1;
211 }
Zesstra2504b2d2020-05-22 12:30:17 +0200212 else if (str == "aus")
213 {
214 if (stat_reports & DO_REPORT_HP || stat_reports & DO_REPORT_WIMPY)
215 {
MG Mud User88f12472016-06-24 23:31:02 +0200216 string s="";
Zesstra2504b2d2020-05-22 12:30:17 +0200217 if (stat_reports & DO_REPORT_HP)
218 {
MG Mud User88f12472016-06-24 23:31:02 +0200219 str="ebenfalls ";
220 tell_object(ME, "Der Report wurde ausgeschaltet.\n");
221 }
Zesstra2504b2d2020-05-22 12:30:17 +0200222 if ( stat_reports & DO_REPORT_WIMPY )
223 {
MG Mud User88f12472016-06-24 23:31:02 +0200224 tell_object(ME, "Der Vorsicht-Report wurde "+s+
225 "ausgeschaltet.\n");
226 }
227 stat_reports=0;
228 }
Zesstra2504b2d2020-05-22 12:30:17 +0200229 else
230 {
MG Mud User88f12472016-06-24 23:31:02 +0200231 tell_object(ME, "Der Report ist bereits ausgeschaltet.\n");
232 }
233 return 1;
234 }
Zesstra2504b2d2020-05-22 12:30:17 +0200235 else if (str == "ein")
236 {
237 if ( stat_reports & DO_REPORT_HP )
238 {
MG Mud User88f12472016-06-24 23:31:02 +0200239 tell_object(ME, "Der Report ist bereits eingeschaltet.\n");
240 return 1;
241 }
242 tell_object(ME, "Der Report wurde eingeschaltet.\n");
243 stat_reports |= DO_REPORT_HP;
Zesstra2504b2d2020-05-22 12:30:17 +0200244 if (!(canflags & CAN_REPORT_SP))
245 {
246 if (QueryQuest("Hilf den Gnarfen")==1)
247 {
MG Mud User88f12472016-06-24 23:31:02 +0200248 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_SP);
249 stat_reports |= DO_REPORT_SP;
250 }
Zesstra2504b2d2020-05-22 12:30:17 +0200251 else
252 {
MG Mud User88f12472016-06-24 23:31:02 +0200253 tell_object(ME, break_string(
254 "Fuer den Statusreport Deiner Konzentration musst Du jedoch "
255 "zunaechst die Quest \"Hilf den Gnarfen\" bestehen.",78));
256 }
257 }
Zesstra2504b2d2020-05-22 12:30:17 +0200258 else
259 {
MG Mud User88f12472016-06-24 23:31:02 +0200260 stat_reports |= DO_REPORT_SP;
261 }
Zesstra2504b2d2020-05-22 12:30:17 +0200262 if (!(canflags & CAN_REPORT_POISON))
263 {
264 if (QueryQuest("Katzenjammer")==1)
265 {
MG Mud User88f12472016-06-24 23:31:02 +0200266 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_POISON);
267 stat_reports |= DO_REPORT_POISON;
268 }
Zesstra2504b2d2020-05-22 12:30:17 +0200269 else
270 {
MG Mud User88f12472016-06-24 23:31:02 +0200271 tell_object(ME, break_string(
272 "Fuer den Statusreport Deiner Vergiftung musst Du jedoch "
273 "zunaechst die Quest \"Katzenjammer\" bestehen.",78));
274 }
275 }
Zesstra2504b2d2020-05-22 12:30:17 +0200276 else
277 {
MG Mud User88f12472016-06-24 23:31:02 +0200278 stat_reports |= DO_REPORT_POISON;
279 }
280 // Cache loeschen, damit beim naechsten Report-Event alle Daten neu
281 // eingetragen werden muessen. Muss beim Einschalten des Reports
282 // passieren, weil auch in der inaktiven Zeit weiterhin Aenderungen in
283 // status_report() eingehen, so dass der Cache zwar erst einmal leer ist,
284 // aber beim Wiedereinschalten nicht mehr ungueltig waere und somit
285 // veraltete Daten an den Spieler ausgegeben werden. Im unguenstigsten
286 // Fall wuerde das sogar dazu fuehren, dass die veralteten Daten lange
287 // Zeit nicht aktualisiert werden, wenn z.B. P_HP == P_MAX_HP, so dass
288 // kein P_HP-Event mehr eingeht.
289 report_cache=0;
Zesstra2504b2d2020-05-22 12:30:17 +0200290 // Fall-through fuer Statusausgabe
MG Mud User88f12472016-06-24 23:31:02 +0200291 }
Zesstra2504b2d2020-05-22 12:30:17 +0200292 else if (str == "vorsicht")
293 {
294 if (!(canflags & CAN_REPORT_WIMPY))
295 {
296 if (QueryQuest("Schrat kann nicht einschlafen")==1)
297 {
MG Mud User88f12472016-06-24 23:31:02 +0200298 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_WIMPY);
299 tell_object(ME, "Der Vorsicht-Report wurde eingeschaltet.\n");
300 stat_reports |= DO_REPORT_WIMPY;
301 }
Zesstra2504b2d2020-05-22 12:30:17 +0200302 else
303 {
MG Mud User88f12472016-06-24 23:31:02 +0200304 tell_object(ME, break_string(
305 "Fuer den Statusreport Deiner Vorsicht musst Du "
306 "zunaechst die Quest \"Schrat kann nicht einschlafen\" "
307 "bestehen.",78));
308 }
309 }
310 else
311 {
312 stat_reports |= DO_REPORT_WIMPY;
313 }
314 // fuer Seher auch Bericht der Fluchtrichtung einschalten.
315 if ((stat_reports & DO_REPORT_WIMPY)
316 && !(stat_reports & DO_REPORT_WIMPY_DIR)
317 && ((canflags & CAN_REPORT_WIMPY) || IS_SEER(ME)))
318 {
Zesstra2504b2d2020-05-22 12:30:17 +0200319 stat_reports |= DO_REPORT_WIMPY_DIR;
MG Mud User88f12472016-06-24 23:31:02 +0200320 }
Zesstra2504b2d2020-05-22 12:30:17 +0200321 // Fall-through fuer Statusausgabe
MG Mud User88f12472016-06-24 23:31:02 +0200322 }
323 // sendet einmalig genau jetzt den konfigurierten report. Kann zum testen
324 // (von Triggern) oder beim Login benutzt werden, wenn man einen initialen
325 // Datenbestand erhalten will.
326 else if (str=="senden")
327 {
328 // Es wird Ausgabe von LP und Vorsicht getriggert, das sendet beide
329 // Zeilen.
330 status_report(DO_REPORT_HP, QueryProp(P_HP));
331 status_report(DO_REPORT_WIMPY, QueryProp(P_WIMPY));
332 return 1;
333 }
334 else
335 return 0;
336 // nur aktuellen Zustand berichten
337 set_report(0);
338 return 1;
339}
340
341private string get_poison_desc(int p) {
342 string ret;
343 if ( intp(p) ) {
344 switch(p) {
345 case 0: ret="keins"; break;
346 case 1..3: ret="leicht"; break;
347 case 4..8: ret="gefaehrlich"; break;
348 default: ret="sehr ernst"; break;
349 }
350 return ret;
351 }
352 else return "(nicht verfuegbar)";
353}
354
355// sprintf()-Formatstrings fuer die Reportausgabe.
356#define REPORTLINE "LP: %3d, KP: %3s, Gift: %s.\n"
357#define REPORTLINE_WIMPY "Vorsicht: %d, Fluchtrichtung: %s.\n"
358// Defines zur Adressierung der Cache-Eintraege
359#define REP_HP 0
360#define REP_SP 1
361#define REP_POISON 2
362
363protected void status_report(int type, mixed val) {
364 // Wenn der Spieler GMCP hat und das sich um die Information kuemmert,
365 // erfolgt keine textuelle Ausgabe mehr. Daher return, wenn GMCP_Char()
366 // erfolg vermeldet hat.
367 int flags = QueryProp(P_CAN_FLAGS);
368 switch (type) {
369 case DO_REPORT_HP:
370 if (GMCP_Char( ([ P_HP: val ]) ) ) return;
371 break;
372 case DO_REPORT_SP:
373 if (!(flags & CAN_REPORT_SP)) return;
374 if (GMCP_Char( ([ P_SP: val ]) ) ) return;
375 break;
376 case DO_REPORT_POISON:
377 if (!(flags & CAN_REPORT_POISON)) return;
378 if (GMCP_Char( ([ P_POISON: val ]) ) ) return;
379 break;
380 case DO_REPORT_WIMPY:
381 if (!(flags & CAN_REPORT_WIMPY)) return;
382 if (GMCP_Char( ([ P_WIMPY: val ]) ) ) return;
383 break;
384 case DO_REPORT_WIMPY_DIR:
385 if (!(flags & CAN_REPORT_WIMPY_DIR)) return;
386 if (GMCP_Char( ([ P_WIMPY_DIRECTION: val ]) ) ) return;
387 break;
388 }
389
390 // konventionelle textuelle Ausgabe des Reports ab hier.
391 if (!(type & stat_reports))
392 return;
393
394 if ( !report_cache ) {
395 report_cache = ({
396 QueryProp(P_HP),
397 (stat_reports&DO_REPORT_SP) ? to_string(QueryProp(P_SP)) : "###",
398 (stat_reports&DO_REPORT_POISON) ?
399 get_poison_desc(QueryProp(P_POISON)) : "(nicht verfuegbar)"
400 });
401 }
402
403 switch(type) {
404 // LP berichten: Cache aktualisieren und Meldung ausgeben.
405 case DO_REPORT_HP:
406 report_cache[REP_HP]=val;
407 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
408 report_cache[REP_SP], report_cache[REP_POISON]));
409 break;
410 // KP berichten: Wenn der Spieler den Report freigeschaltet hat,
411 // wird bei Aenderungen gemeldet. Wenn nicht, aendert sich nur der
412 // Cache-Eintrag. So wird verhindert, dass ein Spieler ueber KP-
413 // Veraenderungen auch dann informiert wuerde, wenn er den KP-Report
414 // gar nicht benutzen koennte.
415 case DO_REPORT_SP:
416 report_cache[REP_SP]=to_string(val);
417 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
418 report_cache[REP_SP], report_cache[REP_POISON]));
419 break;
420 // Giftstatus berichten: Wenn der Giftreport freigeschaltet ist,
421 // Cache aktualisieren und berichten. Wenn nicht, aendert sich nur
422 // der Cache-Eintrag. Erlaeuterung hierzu s.o. beim KP-Report.
423 case DO_REPORT_POISON:
424 report_cache[REP_POISON] = get_poison_desc(val);
425 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
426 report_cache[REP_SP], report_cache[REP_POISON]));
427 break;
428 // Vorsicht-Report: kann ohne weitere Abfragen ausgegeben werden, da
429 // alle noetigen Checks schon zu Beginn dieser Funktion erledigt wurden.
430 // Lediglich der Inhalt der Meldung muss abhaengig vom Seherstatus
431 // konfiguriert werden.
432 case DO_REPORT_WIMPY:
433 string res;
434 if (IS_SEER(ME)) {
435 // QueryProp() aus Kostengruenden im if(), damit die Aufruf-
436 // Haeufigkeit zumindest ein wenig reduziert wird.
437 string dir = QueryProp(P_WIMPY_DIRECTION)||"keine";
438 res = sprintf(REPORTLINE_WIMPY, val, dir);
439 }
440 else
441 res = sprintf(REPORTLINE_WIMPY, val, "(nicht verfuegbar)");
442 tell_object(ME, res);
443 break;
444 // Fluchtrichtungs-Report: wird nur bei Sehern ausgegeben, damit
445 // nicht auch Spieler eine VS-/FR-Meldung bekommen, wenn z.B. eine
446 // externe Manipulation der Fluchtrichtung stattfindet, sie aber den
447 // Report mangels Seherstatus gar nicht freigeschaltet haben.
448 case DO_REPORT_WIMPY_DIR:
449 if (IS_SEER(ME)) {
450 if (!val) val = "keine";
451 tell_object(ME,sprintf(REPORTLINE_WIMPY, QueryProp(P_WIMPY), val));
452 }
453 break;
454 }
455}
456
457#undef REPORTLINE
458#undef REPORTLINE_WIMPY
459#undef REP_HP
460#undef REP_SP
461#undef REP_POISON
462
463private string permutate(string msg)
464{
465 // Kontrollzeichen rausfiltern. *seufz*
466 msg = regreplace(msg,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
467 object ob=QueryProp(P_PERM_STRING);
468 if (!objectp(ob))
469 return msg;
470
Zesstra04f613c2019-11-27 23:32:54 +0100471 return ({string})ob->permutate_string(msg)||"";
MG Mud User88f12472016-06-24 23:31:02 +0200472}
473
474// neue nachricht an den Kobold anhaengen
475// Rueckgabewerte: MSG_BUFFER_FULL oder MSG_BUFFERED
476private int add_to_kobold(string msg, int msg_type, string msg_action,
477 string msg_prefix, object origin)
478{
479 // Nachricht soll im Kobold gespeichert werden.
480 // Kobold speichert Rohdaten und gibt spaeter das ganze auch wieder via
481 // ReceiveMsg() aus - dabei wird MSG_DONT_BUFFER | MSG_DONT_STORE gesetz,
482 // damit keine erneute Speicher in Kobold oder Komm-History erfolgt.
483
484 // wenn der Puffer zu klein ist, Groesse verdoppeln, wenn noch unterhalb
485 // des Limits.
486 if (kobold->index >= sizeof(kobold->buf)-1) {
487 if (sizeof(kobold->buf) < MAX_KOBOLD_LIMIT)
488 kobold->buf += allocate(sizeof(kobold->buf));
489 else
490 return MSG_BUFFER_FULL;
491 }
492 kobold->index = kobold->index +1;
493 // neue Nachricht an den Puffer anhaengen.
494 string sendername = query_once_interactive(origin) ?
495 origin->query_real_name() :
496 origin->name(WER) || "<Unbekannt>";
Zesstraa5fda4a2022-01-06 17:31:44 +0100497 kobold->buf[kobold->index] = (<kobold_msg_s> msg: msg,
MG Mud User88f12472016-06-24 23:31:02 +0200498 type : msg_type, action : msg_action, prefix : msg_prefix,
499 sendername : sendername);
500 return MSG_BUFFERED;
501}
502
Zesstra7459f252022-02-23 22:47:26 +0100503// speichert den Inhalt vom commvault im Kobold und der TM-History
504private void process_comm_vault(lwobject "/std/player/comm_vault" vault)
505{
506 struct kobold_msg_s *buffer = vault.Retrieve();
507 if (!sizeof(buffer))
508 return;
509 foreach(struct kobold_msg_s msg: buffer)
510 {
511 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
512 int res = check_ignores(msg.msg, msg.type, msg.action, msg.prefix,
513 msg.sendername);
514 if (res) {
515 // Nachricht wegwerfen. Aber ggf. den Absender informieren, wenn der
516 // online ist und wir nicht Invis
517 object pl = find_player(msg.sendername);
518 if (pl &&
519 (!QueryProp(P_INVIS) || IS_LEARNER(pl)) )
520 pl->ReceiveNotify(sprintf("Deine Nachricht an %s wurde "
521 "ignoriert.",capitalize(getuid(this_object()))), MA_TELL);
522 continue;
523 }
524
525 // wenn der Puffer zu klein ist, Groesse verdoppeln.
526 // Keine Pruefung hier, weil das vault schon die Groesse beschraenkt und
527 // der Inhalt auf jeden Fall passen soll.
528 if (kobold->index >= sizeof(kobold->buf)-1)
529 kobold->buf += allocate(sizeof(kobold->buf));
530 kobold->index += 1;
531 kobold->buf[kobold->index] = msg;
532
533 // TM-History
534 add_struct_tell_history(msg, 0, 1, MSGFLAG_TELL);
535 }
536 vault.Empty();
537}
538
MG Mud User88f12472016-06-24 23:31:02 +0200539private void _flush_cache(int verbose) {
540 // nur mit genug Evalticks ausgeben.
Zesstra7459f252022-02-23 22:47:26 +0100541 if (get_eval_cost() < 500000) return;
MG Mud User88f12472016-06-24 23:31:02 +0200542 if (kobold->index >= 0)
543 {
544 ReceiveMsg("Ein kleiner Kobold teilt Dir folgendes mit:",
545 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
546 0, 0, this_object());
547 int prepend = QueryProp(P_MESSAGE_PREPEND);
548 foreach(int i: 0 .. kobold->index) // '0 ..' ist wichtig!
549 {
Zesstraa5fda4a2022-01-06 17:31:44 +0100550 struct kobold_msg_s msg = kobold->buf[i];
MG Mud User88f12472016-06-24 23:31:02 +0200551 // dies ist dient der Fehlerabsicherung, falls es nen Fehler (z.B. TLE)
552 // in der Schleife unten gab: dann ist index nicht auf -1 gesetzt
553 // worden, aber einige Nachrichten sind schon geloescht.
554 if (!structp(msg)) continue;
Zesstra7459f252022-02-23 22:47:26 +0100555 // Im folgenden nicht den string in der struct aendern (die wird ggf.
556 // auch noch von der TM-History gebraucht).
557 string msgstr = msg.msg;
558 // Wenn Nachricht schon laenger her ist, Uhrzeit anhaengen, aber ggf.
559 // muss ein \n abgeschnitten werden.
560 if (msg.timestamp < time() - 3600)
561 {
562 if (msgstr[<1] == '\n')
563 msgstr = msgstr[0..<2]
564 + " [" + strftime("%d.%m.%y %T", msg.timestamp) + "]";
565 else
566 msgstr = msgstr
567 + " [" + strftime("%d.%m.%y %T", msg.timestamp) + "]";
568 }
MG Mud User88f12472016-06-24 23:31:02 +0200569 // Ausgabe via efun::tell_object(), weil die Arbeit von ReceiveMsg()
Zesstra3a261e52022-02-10 14:00:31 +0100570 // schon getan wurde. Allerdings muessen wir uns noch um den Umbruch
571 // und Farben kuemmern.
Zesstra7459f252022-02-23 22:47:26 +0100572 msgstr = terminal_colour(msgstr, colourmap);
MG Mud User88f12472016-06-24 23:31:02 +0200573 if ((msg->type) & MSG_DONT_WRAP)
Zesstra7459f252022-02-23 22:47:26 +0100574 msgstr = (msg->prefix ? msg->prefix : "") + msgstr;
MG Mud User88f12472016-06-24 23:31:02 +0200575 else
576 {
577 int bsflags = msg->type & MSG_ALL_BS_FLAGS;
578 if (prepend)
579 bsflags |= BS_PREPEND_INDENT;
Zesstra7459f252022-02-23 22:47:26 +0100580 msgstr = break_string(msgstr, 78, msg->prefix, bsflags);
MG Mud User88f12472016-06-24 23:31:02 +0200581 }
Zesstra7459f252022-02-23 22:47:26 +0100582 efun::tell_object(this_object(), msgstr);
MG Mud User88f12472016-06-24 23:31:02 +0200583 kobold->buf[i]=0;
584 }
585 kobold->index=-1;
586 }
587 else if (verbose)
588 {
589 ReceiveMsg("Der kleine Kobold hat leider nichts Neues fuer Dich.",
590 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
591 0, 0, this_object());
592 }
593}
594
595varargs int cmd_kobold(string arg)
596{
Zesstra7459f252022-02-23 22:47:26 +0100597 if (!sizeof(arg))
598 {
599 _flush_cache(1);
600 return 1;
601 }
MG Mud User88f12472016-06-24 23:31:02 +0200602 switch(arg)
603 {
604 case "ein":
Zesstra7459f252022-02-23 22:47:26 +0100605 SetProp(P_BUFFER, KOBOLD_ONLINE|KOBOLD_OFFLINE);
606 ReceiveNotify("Der Kobold merkt sich jetzt alles!");
607 break;
608 case "online":
609 SetProp(P_BUFFER, KOBOLD_ONLINE);
610 ReceiveNotify("Der Kobold merkt sich jetzt alles, "
611 "wenn Du online bist!");
612 break;
613 case "offline":
614 SetProp(P_BUFFER, KOBOLD_OFFLINE);
615 ReceiveNotify("Der Kobold merkt sich jetzt alles, "
616 "wenn Du offline bist!");
617 break;
MG Mud User88f12472016-06-24 23:31:02 +0200618 case "aus":
619 SetProp(P_BUFFER, 0);
Zesstra7459f252022-02-23 22:47:26 +0100620 ReceiveNotify("Der Kobold wird Dich nicht stoeren!");
621 break;
622 default:
623 ReceiveNotify("Der Kobold sagt: Was soll ich mir denn merken? "
624 "('ein', 'aus', 'offline' oder 'online')");
625 return 1;
MG Mud User88f12472016-06-24 23:31:02 +0200626 }
Zesstra7459f252022-02-23 22:47:26 +0100627 if (QueryProp(P_BUFFER) & KOBOLD_OFFLINE)
628 setup_comm_vault();
629 else
630 {
631 // Comm-Vault entfernen. Aber zur Sicherheit nochmal abrufen und
632 // verarbeiten (sollte aber eigentlich ueberfluessig sein)
633 commvault = KOBOLD->ForgetVault();
634 if (commvault) {
635 process_comm_vault(commvault);
636 commvault = 0;
637 }
638 }
MG Mud User88f12472016-06-24 23:31:02 +0200639 return 1;
640}
641
642public int TestIgnoreSimple(string *arg)
Zesstrade642d22019-11-23 17:22:34 +0100643{ mapping ignore;
MG Mud User88f12472016-06-24 23:31:02 +0200644
645 if (!pointerp(arg) || !mappingp(ignore=Query(P_IGNORE,F_VALUE)))
646 return 0;
647 foreach(string s: arg)
648 {
649 if (member(ignore,s))
650 return 1;
651 }
652 return 0;
653}
654
655//TODO: deprecated - entfernen, wenn Message() entfernt wird.
656private int check_ignore(mixed ignore, string verb, string name)
657{
658 if (ignore == verb)
659 return 1;
660 ignore = explode(ignore, ".");
661 return ((sizeof(ignore) > 1) &&
662 (name == ignore[0] && member(ignore[1..], verb) != -1));
663}
664
Zesstra3a261e52022-02-10 14:00:31 +0100665// Die Nachricht kommt mit einem Alert oder Mention. comm_beep() prueft, ob
666// ein Piepston ausgegeben werden soll (langfristig andere Benachrichtigungen
667// geplant!).
668// Ueblicherweise werden nur alle <P_MESSAGE_BEEP> Sekunden Toene
669// uebermittelt.
670private void comm_beep(string msg_action, int mention=0)
Bugfix60f5bc62022-01-21 11:09:56 +0100671{
Zesstra1cc6cf32022-02-09 11:49:31 +0100672 // Wenn Alerts komplett abgeschaltet sind, gibts nix.
673 int alert_config = ({int})QueryProp(P_ALERT);
674 if (alert_config & AL_NO_SOUND)
675 return; // kein ton
676 // und maximal alle P_MESSAGE_BEEP Sekunden aufmerksam machen; bei 0s ist
677 // jedes Mal erlaubt.
Zesstra04f613c2019-11-27 23:32:54 +0100678 int beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
Zesstra1cc6cf32022-02-09 11:49:31 +0100679 if ((time()-last_beep_time) < beep_interval)
680 return;
681 int required;
682 switch(msg_action)
Bugfix60f5bc62022-01-21 11:09:56 +0100683 {
Zesstra1cc6cf32022-02-09 11:49:31 +0100684 case MA_TELL:
685 required |= MB_TELL;
686 break;
687 case MA_SAY:
688 required |= MB_SAY;
689 break;
690 case MA_CHANNEL:
691 required |= MB_CHANNEL;
692 break;
693 case MA_SHOUT:
694 required |= MB_SHOUT;
695 break;
696 default:
697 // Alle anderen Aktionen, die keine gesonderten Aktionsfilter haben
698 required |= MB_MISC;
699 break;
Bugfix60f5bc62022-01-21 11:09:56 +0100700 }
Zesstra3a261e52022-02-10 14:00:31 +0100701 if (mention)
702 required |= MB_MENTION;
703
Zesstra1cc6cf32022-02-09 11:49:31 +0100704 // wenn in alert min. ein notwendiges Flag drin ist darf gepiepst werden
705 if (required & alert_config)
706 {
707 last_beep_time = time();
708 binary_message(b"\a", 1);
709 }
MG Mud User88f12472016-06-24 23:31:02 +0200710}
711
Zesstra7459f252022-02-23 22:47:26 +0100712private varargs void add_struct_tell_history(struct kobold_msg_s msg,
713 int sent, int recv, int flags )
MG Mud User88f12472016-06-24 23:31:02 +0200714{
715 /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
716 Als Wert ist eine Strukur vom Typ chat_s eingetragen.
Zesstra7459f252022-02-23 22:47:26 +0100717 Strukturen chat_s und kobold_msg_s sind in /std/player/comm_structs.c
MG Mud User88f12472016-06-24 23:31:02 +0200718 definiert.
719 TODO fuer spaeter, gerade keine Zeit fuer:
720 Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
721 jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
722 weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
723 Element ist aeltestes Gespraech).
724 */
725
Zesstra996bafe2022-02-14 22:42:39 +0100726 // Gespraechspartner fuer erwidere auch ohne tmhist speichern.
Zesstraa31cd5c2016-12-17 20:11:07 +0100727 if (flags & (MSGFLAG_TELL|MSGFLAG_RTELL))
Zesstra7459f252022-02-23 22:47:26 +0100728 last_comm_partner = msg.sendername;
MG Mud User88f12472016-06-24 23:31:02 +0200729
730 // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
731 // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
732 // Anfang.
733 if (sent) {
734 if (!sizeof(commreceivers))
Zesstra7459f252022-02-23 22:47:26 +0100735 commreceivers = ({msg.sendername});
736 else if (commreceivers[0] != msg.sendername) {
MG Mud User88f12472016-06-24 23:31:02 +0200737 // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
738 // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
739 // entfernen.
740 // TODO: Effizienter gestalten.
Zesstra7459f252022-02-23 22:47:26 +0100741 commreceivers = ({msg.sendername}) + (commreceivers-({msg.sendername}));
MG Mud User88f12472016-06-24 23:31:02 +0200742 // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
743 // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
744 // commreceivers drin sind.)
745 if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
746 commreceivers = commreceivers[0..MAX_SAVED_CHATS];
747 }
748 }
749
Zesstra996bafe2022-02-14 22:42:39 +0100750 // mit eingeschalteter History weitere Details speichern.
MG Mud User88f12472016-06-24 23:31:02 +0200751 if (!tell_history_enabled)
752 return;
753
Zesstra7459f252022-02-23 22:47:26 +0100754 if (msg.msg[<1] == '\n')
755 msg.msg = msg.msg[..<2];
MG Mud User88f12472016-06-24 23:31:02 +0200756
757 struct chat_s chat;
758 // Gespraechspartner unbekannt?
Zesstra7459f252022-02-23 22:47:26 +0100759 if (!member(tell_history, msg.sendername)) {
MG Mud User88f12472016-06-24 23:31:02 +0200760 // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
761 // dazu kommt.
762 if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
763 string deluid;
764 int zeit = __INT_MAX__;
765 foreach(string tuid, chat : tell_history) {
766 // aeltestes Gespraech suchen
767 if (zeit > chat->time_last_msg) {
768 deluid = tuid;
769 zeit = chat->time_last_msg;
770 }
771 }
772 // aeltestes Gespraech raus.
773 m_delete(tell_history, deluid);
774 if (member(commreceivers,deluid)>-1)
775 commreceivers-=({deluid});
776 }
777 // neues Gespraech anlegen
Zesstra7459f252022-02-23 22:47:26 +0100778 chat = (<chat_s> uid: msg.sendername, time_first_msg: msg.timestamp,
779 time_last_msg: msg.timestamp,
MG Mud User88f12472016-06-24 23:31:02 +0200780 sentcount: sent, recvcount: recv,
781 msgbuf: 0, ptr: 0 );
Zesstra7459f252022-02-23 22:47:26 +0100782 tell_history[msg.sendername] = chat;
MG Mud User88f12472016-06-24 23:31:02 +0200783 }
784 else {
785 // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
Zesstra7459f252022-02-23 22:47:26 +0100786 chat = tell_history[msg.sendername];
787 chat->time_last_msg = msg.timestamp;
MG Mud User88f12472016-06-24 23:31:02 +0200788 chat->sentcount += sent;
789 chat->recvcount += recv;
790 }
791
Zesstra996bafe2022-02-14 22:42:39 +0100792 // ggf. auch INhalte von Gespraechen speichern
MG Mud User88f12472016-06-24 23:31:02 +0200793 if (tell_history_enabled < TELLHIST_ENABLED)
794 return;
795
796 // ggf. Array fuer Messages anlegen
797 if (!pointerp(chat->msgbuf))
798 chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
799
Zesstra7459f252022-02-23 22:47:26 +0100800 // neue Struct ins Array schreiben
801 chat->msgbuf[chat->ptr] = msg;
MG Mud User88f12472016-06-24 23:31:02 +0200802 // Index auf naechste Messagestruktur ermitteln
803 chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
Zesstra7459f252022-02-23 22:47:26 +0100804}
805
806private varargs void add_to_tell_history( string uid, int sent, int recv,
807 string message, string indent, int flags )
808{
809 //TODO: Entfernen, wenn das nicht mehr passiert.
810 if (!stringp(uid))
811 {
812 ReceiveMsg(sprintf(
813 "\nadd_to_tell_history(): got bad uid argument %O."
814 "sent: %d, recv: %d, flags: %d, msg: %s",
815 uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);
816 }
817 // Message-Struktur anlegen
818 struct kobold_msg_s msg = (<kobold_msg_s> msg: message, prefix: indent,
819 sendername: uid, timestamp: time());
820 add_struct_tell_history(msg, sent, recv, flags);
MG Mud User88f12472016-06-24 23:31:02 +0200821}
822
Zesstra996bafe2022-02-14 22:42:39 +0100823protected void clear_tell_history(int force)
MG Mud User88f12472016-06-24 23:31:02 +0200824{
825 /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
826 sofern der Spieler nichts abweichendes eingestellt hat. */
827
Zesstra996bafe2022-02-14 22:42:39 +0100828 // bei manuellem Loeschen (force==1) immer loeschen, ansonsten nur, wenn die
829 // History nicht "long-life" ist.
830 if (tell_history_enabled < TELLHIST_LONGLIFE || force)
831 {
832 tell_history = ([]);
833 commreceivers = ({});
834 }
MG Mud User88f12472016-06-24 23:31:02 +0200835}
836
837protected void reset(void)
838{
839 /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
840 Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
841 wie bei einem "schlafe ein" die Mitteilungen geloescht. */
842
843 if (!interactive())
Zesstra996bafe2022-02-14 22:42:39 +0100844 clear_tell_history(0);
MG Mud User88f12472016-06-24 23:31:02 +0200845
846}
847
848// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
849// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
850// werden soll.
851private void _send(object ob, string msg, int msg_type,
852 string msg_action, string msg_prefix)
853{
854 int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
Zesstra8b5320e2022-02-18 21:10:26 +0100855
MG Mud User88f12472016-06-24 23:31:02 +0200856 switch(res) {
857 case MSG_DELIVERED:
858 break; // nix machen
859 case MSG_BUFFERED:
Zesstra62b4a862022-12-23 20:08:16 +0100860 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200861 "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
862 "genommen. Er wird sie spaeter weiterleiten!",
863 MT_NOTIFICATION, msg_action, 0, this_object());
864 break;
865 case MSG_IGNORED:
866 case MSG_VERB_IGN:
867 case MSG_MUD_IGN:
868 ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
869 MT_NOTIFICATION, msg_action, 0, this_object());
870 break;
871 case MSG_SENSE_BLOCK:
872 ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
873 MT_NOTIFICATION, msg_action, 0, this_object());
874 break;
875 case MSG_BUFFER_FULL:
Zesstra62b4a862022-12-23 20:08:16 +0100876 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200877 "Die Mitteilung ging verloren, denn der Kobold kann sich "
878 "nichts mehr merken!", MT_NOTIFICATION, msg_action,
879 0, this_object());
880 break;
881 default:
882 ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
883 "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
884 break;
885 }
886}
887
Zesstra8b5320e2022-02-18 21:10:26 +0100888// aehnlich wie _send(), aber gerufen, wenn die Empfaengerin nicht online ist,
889// aber ein Kobold-Vault deponiert hat und versucht werden soll, dort eine
890// Nachricht zu hinterlegen.
891private void _send_to_kobold(string pluid, string msg, int msg_type,
892 string msg_action, string msg_prefix)
893{
894 int res = KOBOLD->DepositMsg(pluid, msg, msg_type, msg_action,
895 msg_prefix, ME);
896 switch(res) {
897 case MSG_BUFFERED:
898 ReceiveMsg(sprintf("%s ist gerade nicht online, aber Deine Botschaft "
899 "wurde von einem fuer kleinen Kobold aufgeschrieben und er wird "
900 "versuchen, sie %s bei naechster Gelegenheit zu uebermitteln. "
901 "Der Kobold macht Dich darauf aufmerksam, dass er aber keine "
902 "Uebermittlung garantieren kann.",
903 capitalize(pluid), capitalize(pluid)),
904 MT_NOTIFICATION, msg_action, 0, this_object());
905 break;
906 case MSG_BUFFER_FULL:
907 ReceiveMsg(sprintf("%s ist gerade nicht online und leider ist auf "
908 "der Schriftrolle des kleinen Kobolds kein Platz mehr fuer "
909 "Deine Botschaft. Vielleicht schickst Du %s besser einen Brief.",
910 capitalize(pluid), capitalize(pluid)),
911 MT_NOTIFICATION, msg_action, 0, this_object());
912 break;
913 default:
914 ReceiveMsg(sprintf("%s ist gerade nicht online und leider konnte "
915 "sich der kleine Kobold Deine Botschaft nicht merken. "
916 "Vielleicht schickst Du %s besser einen Brief.",
917 capitalize(pluid),capitalize(pluid)),
918 MT_NOTIFICATION, msg_action, 0, this_object());
919 break;
920 }
921}
922
MG Mud User88f12472016-06-24 23:31:02 +0200923// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
924// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
925// die Nachricht ggf. in den Kommhistory erfasst wird.
926// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
927protected varargs int _recv(object ob, string message, int flag, string indent)
928{
929 write(break_string(message, 78, indent,
930 QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
931 if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
932 query_once_interactive(ob))
933 {
934 if (flag & MSGFLAG_WHISPER)
935 add_to_tell_history(getuid(ob), 1, 0,
936 "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
937 flag);
938 else
939 add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
940 }
941 return 1;
942}
943
944// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
945// Fehlerbehandlung) ist es jedoch auch mal ein String.
946varargs int Message(string msg, int flag, string indent,
947 string cname, mixed sender)
948{
949 object ti;
950 string verb, reply, *ignore, tin;
951 int em, te;
952 mixed deaf;
953
954 // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
955 // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
956 // damit 'ignoriere name.debug' funktioniert.
957 if( flag == MSGFLAG_CHANNEL ){
958 if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
959 || msg[1..9]=="Warnungen"))
960 {
961 // Missbrauch der Variable 'ignore' als Zwischenspeicher
962 ignore = regexplode( msg, ":| |\\]" );
963 verb = lower_case(ignore[0][1..]);
964 tin = lower_case(ignore[2]);
965 }
966 else
967 {
968 if(cname)
969 verb=lower_case(cname);
970 else
971 verb=query_verb();
972 if( ti = this_interactive() )
973 {
974 tin = getuid(this_interactive());
975 }
976 else
977 {
978 //falls doch kein Objekt...
979 if (objectp(sender))
980 tin=lower_case(sender->name(RAW)||"<Unbekannt>");
981 }
982 }
983 }
984 else {
985 if( ti = this_interactive() )
986 tin = getuid(this_interactive());
987 verb = query_verb();
988 }
989
990 te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
991
992 // fuer "erwidere"
993 if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
994 if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
995 if (flag & MSGFLAG_WHISPER)
996 add_to_tell_history(getuid(ti), 0, 1,
997 capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
998 (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
999 ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
1000 " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
1001 else
1002 add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
1003 }
1004 }
1005 // Hoert der Spieler nicht?
1006 em = (ti &&
1007 (te || flag & MSGFLAG_SHOUT) &&
1008 (QueryProp(P_EARMUFFS) &&
1009 (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
1010 ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
1011
1012 // Werden der Sender oder das Verb ignoriert?
1013 if(!ti && tin && flag == MSGFLAG_CHANNEL)
1014 {
1015 if((member(ignore, tin) != -1))
1016 {
1017 return MESSAGE_IGNORE_YOU;
1018 }
1019 if(verb && sizeof(filter(ignore, #'check_ignore, verb, tin)) )
1020 {
1021 return MESSAGE_IGNORE_YOU;
1022 }
1023 }
1024 if (ti && (member(ignore, getuid(ti)) != -1)) {
1025 if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
1026 efun::tell_object(ti, capitalize(name())+
1027 " hoert gar nicht zu, was Du sagst.\n");
1028 return MESSAGE_IGNORE_YOU;
1029 }
1030 if(tin && verb &&
1031 sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
1032 {
1033 if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
1034 verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
1035 efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
1036 return MESSAGE_IGNORE_VERB;
1037 }
1038 if (flag & MSGFLAG_RTELL) {
1039 int at;
1040
1041 verb = lower_case(old_explode(msg, " ")[0][1..]);
1042 at = member(verb, '@');
1043 /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
1044 add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
1045
1046 if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
1047 return MESSAGE_IGNORE_YOU;
1048 else if (at > 0 && member(ignore, verb[at..]) >= 0)
1049 return MESSAGE_IGNORE_MUD;
1050 }
1051
1052 // Taubheit/Oropax
1053 te |= (flag & MSGFLAG_SAY);
1054
1055 if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
1056 deaf = QueryProp(P_DEAF);
1057 if (te)
1058 reply = stringp(deaf) ?
1059 capitalize(sprintf(deaf, name())) :
1060 capitalize(name())+" ist momentan leider taub.\n";
1061 }
1062 else if (em)
1063 reply = capitalize(name())+" hat Oropax in den Ohren.\n";
1064
1065 msg = break_string(msg, 78, indent,
1066 (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
1067
Zesstra7459f252022-02-23 22:47:26 +01001068 if((QueryProp(P_BUFFER) & KOBOLD_ONLINE) &&
MG Mud User88f12472016-06-24 23:31:02 +02001069 (deaf ||
1070 query_editing(this_object()) ||
1071 query_input_pending(this_object())))
1072 {
1073 deaf = MESSAGE_DEAF;
1074 if(flag & MSGFLAG_CACHE)
1075 {
1076 if(!stringp(reply))
1077 reply = name()+" moechte gerade nicht gestoert werden.\n";
1078
1079 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1080
1081 int res = add_to_kobold(msg, 0, 0, 0,
1082 objectp(sender) ? sender : ME);
1083 if(res == MSG_BUFFERED)
1084 {
1085
1086 reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
1087 "genommen.\nEr wird sie spaeter weiterleiten!";
1088 deaf = MESSAGE_CACHE;
1089 }
1090 else {
1091 reply += "Die Mitteilung ging verloren, denn "+
1092 "der Kobold kann sich nichts mehr merken!";
1093 deaf = MESSAGE_CACHE_FULL;
1094 }
1095 if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
1096 efun::tell_object(ti, reply+"\n");
1097 }
1098 return deaf;
1099 }
1100 else if((deaf || em) &&
1101 ( (flag & MSGFLAG_RTELL) ||
1102 (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
1103 if (te && ti)
1104 efun::tell_object(ti, reply);
1105 return MESSAGE_DEAF;
1106 }
1107
1108 _flush_cache(0);
1109 if(te && QueryProp(P_AWAY))
1110 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1111
Zesstra1cc6cf32022-02-09 11:49:31 +01001112 if(!objectp(sender) || sender != ME)
Bugfix60f5bc62022-01-21 11:09:56 +01001113 {
Zesstra1cc6cf32022-02-09 11:49:31 +01001114 if (flag & MSGFLAG_SAY)
1115 comm_beep(MA_SAY);
1116 else if (flag & MSGFLAG_TELL)
1117 comm_beep(MA_TELL);
1118 else if (flag & MSGFLAG_CHANNEL)
1119 comm_beep(MA_CHANNEL);
1120 else if (flag & MSGFLAG_SHOUT)
1121 comm_beep(MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001122 }
1123 efun::tell_object(ME, msg);
1124 return MESSAGE_OK;
1125}
1126
1127static int ignoriere(string str)
1128{
1129 str = _unparsed_args(1);
1130 mapping ignore=Query(P_IGNORE, F_VALUE);
1131
1132 if (!str)
1133 {
1134 string* ignarr = m_indices(ignore);
1135 if (!sizeof(ignarr))
1136 tell_object(ME, "Du ignorierst niemanden.\n");
1137 else
1138 ReceiveMsg("Du ignorierst:\n"
1139 + break_string(CountUp(map(sort_array(ignarr, #'> ),
1140 #'capitalize )
1141 ) + ".",78),
1142 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
1143 0,0,this_object());
1144 return 1;
1145 }
1146 // trim spaces from args and convert to lower case.
1147 str = lower_case(trim(str, TRIM_BOTH));
1148
1149 if (member(ignore, str))
1150 {
1151 RemoveIgnore(str);
1152 tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
1153 }
1154 else if (sizeof(ignore)>100)
1155 {
1156 tell_object(ME, "Du ignorierst schon genuegend!\n");
1157 }
1158 else if (AddIgnore(str) == 1)
1159 {
1160 tell_object(ME,
1161 sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
1162 }
1163 else
1164 {
1165 tell_object(ME,
1166 sprintf("'%s' kannst Du nicht ignorieren.\n",str));
1167 }
1168 return 1;
1169}
1170
1171
Zesstra1cc6cf32022-02-09 11:49:31 +01001172private int _alert_filter_flags(int flag, string text)
Bugfix60f5bc62022-01-21 11:09:56 +01001173{
1174 return (flag & QueryProp(P_ALERT));
1175}
1176
Zesstra1cc6cf32022-02-09 11:49:31 +01001177static int _msg_beep(string str)
1178{
1179 int beep_interval;
1180
1181 notify_fail(
1182 "Syntax:\n"
1183 "- klingelton <1 bis 3600 Sekunden>\n"
1184 "- klingelton aus\n"
1185 "- klingelton ein\n"
1186 "- klingelton <+/-><tm / sag / ebenen / ruf / erwaehnung / sonstige / alle / >\n");
1187
1188 if(!sizeof(str))
1189 return 0;
Zesstrad536c452025-06-28 14:23:30 +02001190 if (!regmatch(str,"^[[:digit:]]", RE_PCRE))
Bugfix60f5bc62022-01-21 11:09:56 +01001191 {
MG Mud User88f12472016-06-24 23:31:02 +02001192 if (str=="aus")
Zesstra1cc6cf32022-02-09 11:49:31 +01001193 SetProp(P_ALERT, QueryProp(P_ALERT) | AL_NO_SOUND);
1194 else if (str=="ein")
1195 SetProp(P_ALERT, QueryProp(P_ALERT) & ~AL_NO_SOUND);
Bugfix60f5bc62022-01-21 11:09:56 +01001196 else
1197 {
1198 mapping flags = ([
Zesstra1cc6cf32022-02-09 11:49:31 +01001199 "tm": MB_TELL, "teilemit": MB_TELL,
1200 "sag": MB_SAY, "sage": MB_SAY,
1201 "ebene": MB_CHANNEL, "ebenen": MB_CHANNEL,
1202 "ruf": MB_SHOUT, "rufe": MB_SHOUT,
Zesstra3a261e52022-02-10 14:00:31 +01001203 "erwaehnung": MB_MENTION, "erwaehnungen": MB_MENTION,
Zesstra1cc6cf32022-02-09 11:49:31 +01001204 "sonstige": MB_MISC, "sonstiges": MB_MISC,
1205 "alle": MB_ALL, "alles": MB_ALL,
1206 ]);
Bugfix60f5bc62022-01-21 11:09:56 +01001207 foreach(string part : explode(str, " ") - ({""}))
1208 {
1209 if(!(part[1..] in flags)) continue;
1210 if(part[0] == '+')
1211 {
1212 SetProp(P_ALERT,
1213 QueryProp(P_ALERT) | flags[part[1..]]);
1214 }
1215 else if(part[0] == '-')
1216 {
1217 SetProp(P_ALERT,
1218 QueryProp(P_ALERT) & ~ flags[part[1..]]);
1219 }
1220 }
Bugfix895dc452025-06-30 17:35:45 +02001221 // 0 hat leider historisch die Bedeutung von MB_MISC, daher muss hier
1222 // statt dessen auf AL_NO_SOUND gesetzt werden.
1223 if(!QueryProp(P_ALERT))
1224 {
1225 SetProp(P_ALERT, AL_NO_SOUND);
1226 }
Bugfix60f5bc62022-01-21 11:09:56 +01001227 }
MG Mud User88f12472016-06-24 23:31:02 +02001228 }
Zesstra1cc6cf32022-02-09 11:49:31 +01001229 else
1230 {
1231 beep_interval = to_int(str);
1232 if(beep_interval >= 0)
1233 {
1234 SetProp(P_MESSAGE_BEEP, beep_interval);
1235 }
1236 }
MG Mud User88f12472016-06-24 23:31:02 +02001237
Zesstra593b2c72019-11-27 23:37:03 +01001238 beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
Bugfix60f5bc62022-01-21 11:09:56 +01001239 mapping text = ([
1240 MB_SAY: "sage",
1241 MB_TELL: "teile mit",
1242 MB_CHANNEL: "Ebenenmeldungen",
Zesstra3a261e52022-02-10 14:00:31 +01001243 MB_MENTION: "Erwaehnungen",
Zesstra1cc6cf32022-02-09 11:49:31 +01001244 MB_SHOUT: "rufe",
1245 MB_MISC: "sonstige",]);
1246 string types = CountUp(map(filter(m_indices(text), #'_alert_filter_flags), text));
Bugfix60f5bc62022-01-21 11:09:56 +01001247 if(!sizeof(types))
1248 {
Bugfix895dc452025-06-30 17:35:45 +02001249 types = "Keine";
Bugfix60f5bc62022-01-21 11:09:56 +01001250 }
1251 ReceiveNotify(
Bugfix895dc452025-06-30 17:35:45 +02001252 "Gewaehlte Klingeltoene: " + types + "\n"
1253 "Zeitabstand: "
1254 + (beep_interval ? "maximal alle " + beep_interval + " Sekunden." : "kein") + "\n"
Zesstra1cc6cf32022-02-09 11:49:31 +01001255 + (QueryProp(P_ALERT) & AL_NO_SOUND ? " Allerdings sind Toene insgesamt "
1256 "bei Dir abgeschaltet. (\'hilfe ton\')" : ""),
Bugfix60f5bc62022-01-21 11:09:56 +01001257 query_verb());
MG Mud User88f12472016-06-24 23:31:02 +02001258 return 1;
1259}
1260
1261static int _msg_prepend(string str) {
MG Mud User88f12472016-06-24 23:31:02 +02001262 notify_fail("Syntax: senderwiederholung ein/aus\n");
1263 if (stringp(str)) {
1264 if (str=="aus")
1265 SetProp(P_MESSAGE_PREPEND,1);
1266 else if (str=="ein")
1267 SetProp(P_MESSAGE_PREPEND,0);
1268 else return 0;
1269 }
1270
Bugfixb1d9b4d2021-09-14 20:07:04 +02001271 ReceiveNotify("Senderwiederholung bei Mitteilungen: "+
Zesstra04f613c2019-11-27 23:32:54 +01001272 (({int})QueryProp(P_MESSAGE_PREPEND) ? "aus" : "ein")+".",
MG Mud User88f12472016-06-24 23:31:02 +02001273 query_verb());
1274
1275 return 1;
1276}
1277
1278static int _communicate(mixed str, int silent)
1279{
1280 string verb;
1281 string myname;
1282 string msg;
1283
1284 if (!str || extern_call()) str=_unparsed_args()||"";
1285 /* str=_unparsed_args()||""; */
1286 verb = query_verb();
1287 if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
1288 if (str==""||str==" "||!str)
1289 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001290 ReceiveNotify("Was willst Du sagen?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001291 return 1;
1292 }
1293 msg=permutate(str);
1294
1295 myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
1296 !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
1297 "":QueryProp(P_PRESAY))+name())||"";
1298
1299 // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
1300 // ist, keine MT_COMM)
Zesstrabd1236a2022-02-09 22:35:59 +01001301 send_room(environment(), msg, MT_LISTEN|MSG_ALERT, MA_SAY,
MG Mud User88f12472016-06-24 23:31:02 +02001302 capitalize(myname)+" sagt: ", ({this_object()}) );
1303
1304 if(!silent)
1305 {
1306 ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1307 MA_SAY, "Du sagst: ", ME);
1308 }
1309 return 1;
1310}
1311
1312static int _shout_to_all(mixed str)
1313{
1314 string pre, myname, realname, wizards_msg, players_msg;
1315 string wizard_prefix, player_prefix;
1316 int chars;
1317
1318 if (!(str=_unparsed_args()))
1319 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001320 ReceiveNotify("Was willst Du rufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001321 return 1;
1322 }
1323 chars=sizeof(str)/2;
1324 if (chars<4) chars=4;
1325 pre = (!IS_LEARNER(ME) ||
1326 QueryProp(P_INVIS) ||
1327 !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
1328 realname = capitalize((pre + capitalize(getuid()))||"");
1329 myname = capitalize(pre + name()||"");
1330 if (QueryProp(P_INVIS))
1331 realname = "("+realname+")";
1332
1333 wizards_msg = permutate(str);
1334 wizard_prefix = myname+" ruft: ";
1335
1336 if(QueryProp(P_FROG)) {
1337 players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
1338 player_prefix = myname+" quakt: ";
1339 }
1340 else {
1341 players_msg = wizards_msg;
1342 player_prefix = wizard_prefix;
1343 }
1344
1345 if(!IS_LEARNER(this_player()))
1346 {
1347 if(QueryProp(P_GHOST)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001348 ReceiveNotify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
MG Mud User88f12472016-06-24 23:31:02 +02001349 MA_SHOUT);
1350 return 1;
1351 }
1352 if (QueryProp(P_SP) <(chars+20))
1353 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001354 ReceiveNotify("Du musst erst wieder magische Kraefte sammeln.",
MG Mud User88f12472016-06-24 23:31:02 +02001355 MA_SHOUT);
Bugfixb1d9b4d2021-09-14 20:07:04 +02001356 ReceiveNotify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
MG Mud User88f12472016-06-24 23:31:02 +02001357 "Ebenen').", MA_SHOUT);
1358 return 1;
1359 }
1360 SetProp(P_SP, QueryProp(P_SP) - chars - 20);
1361 }
1362
1363 ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
Zesstrabd1236a2022-02-09 22:35:59 +01001364 MA_SHOUT, "Du rufst: ", ME);
MG Mud User88f12472016-06-24 23:31:02 +02001365
1366 foreach ( object ob : users()-({this_object()}) )
1367 if ( IS_LEARNER(ob) )
Zesstrabd1236a2022-02-09 22:35:59 +01001368 ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR|MSG_ALERT, MA_SHOUT,
1369 wizard_prefix, this_object());
MG Mud User88f12472016-06-24 23:31:02 +02001370 else
Zesstrabd1236a2022-02-09 22:35:59 +01001371 ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR|MSG_ALERT, MA_SHOUT,
1372 player_prefix, this_object());
MG Mud User88f12472016-06-24 23:31:02 +02001373
1374 return 1;
1375}
1376
1377varargs int _tell(string who, mixed msg)
1378{
1379 object ob;
1380 string away,myname,ret;
MG Mud User88f12472016-06-24 23:31:02 +02001381 string *xname;
1382 int i,visflag;
1383
1384 if (extern_call() && this_interactive()!=ME) return 1;
1385 if (!who || !msg) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001386 ReceiveNotify("Was willst Du mitteilen?",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001387 return 1;
1388 }
1389
1390 if(who == ERWIDER_PARAM)
1391 {
1392 if (!last_comm_partner)
1393 {
1394 _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
1395 "Du was erwidern\nkoenntest.\n");
1396 return 0;
1397 }
1398 who=last_comm_partner;
1399 }
1400
1401 // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
1402 if (who == ".")
1403 who = ".1";
1404
1405 if ( sscanf(who, ".%d", i) == 1 ) {
1406 if(i > 0 && i <= sizeof(commreceivers))
1407 who = commreceivers[i-1];
1408 else {
1409 _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
1410 return 0;
1411 }
1412 }
1413
1414 xname = explode(who, "@");
1415
Zesstrabd1236a2022-02-09 22:35:59 +01001416 if (sizeof(xname) == 2)
MG Mud User88f12472016-06-24 23:31:02 +02001417 {
1418 if ( QueryProp(P_QP) )
1419 {
Zesstra04f613c2019-11-27 23:32:54 +01001420 if (ret=({string})INETD->_send_udp(xname[1],
MG Mud User88f12472016-06-24 23:31:02 +02001421 ([ REQUEST: "tell",
1422 RECIPIENT: xname[0],
1423 SENDER: getuid(ME),
1424 DATA: msg ]), 1))
1425 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001426 ReceiveNotify(ret, MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001427 }
1428 else
1429 {
1430 write("Nachricht abgeschickt.\n");
1431 add_to_tell_history(who, 1, 0, msg,
Zesstraa31cd5c2016-12-17 20:11:07 +01001432 "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_RTELL, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001433 }
1434 }
1435 else
1436 write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
1437 "Muds etwas mitteilen zu koennen.\n");
1438 return 1;
1439 }
1440
Zesstra8b5320e2022-02-18 21:10:26 +01001441 string|int lname = lower_case(who);
1442 if (!ob=find_player(lname))
MG Mud User88f12472016-06-24 23:31:02 +02001443 {
Zesstra8b5320e2022-02-18 21:10:26 +01001444 lname = match_living(lname, 0);
1445 if (!stringp(lname))
1446 {
1447 switch(lname)
1448 {
MG Mud User88f12472016-06-24 23:31:02 +02001449 case -1:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001450 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001451 return 1;
1452 case -2:
Zesstra8b5320e2022-02-18 21:10:26 +01001453 // check KOBOLD
Zesstra7459f252022-02-23 22:47:26 +01001454 ob = load_object(KOBOLD);
Zesstra8b5320e2022-02-18 21:10:26 +01001455 lname = ({string|int})ob->find_player(lower_case(who));
1456 if (lname == -1) {
1457 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
1458 return 1;
1459 }
1460 else if (!stringp(lname)) {
1461 ReceiveNotify("Kein solcher Spieler (online)!",MA_TELL);
1462 return 1;
1463 }
MG Mud User88f12472016-06-24 23:31:02 +02001464 }
Zesstra8b5320e2022-02-18 21:10:26 +01001465 // jetzt ist ob der KOBOLD und lname die UID eines Spielers.
1466 }
1467 // Wenn wir noch kein ob haben, sollte lname jetzt aber durch das
1468 // match_living() der Name eines Livings sein, dessen Objekt wir noch
1469 // brauchen.
1470 if (!ob)
1471 ob = find_player(lname) || find_living(lname);
MG Mud User88f12472016-06-24 23:31:02 +02001472 if(!ob) {
Zesstra8b5320e2022-02-18 21:10:26 +01001473 ReceiveNotify("Kein solcher Spieler (online)!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001474 return 1;
1475 }
1476 }
1477
Zesstra8b5320e2022-02-18 21:10:26 +01001478 if(QueryProp(P_INVIS))
1479 {
MG Mud User88f12472016-06-24 23:31:02 +02001480 if(!IS_LEARNER(ob))
1481 myname = name();
1482 else
1483 myname="("+
1484 ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
1485 capitalize(getuid()) + ")";
1486 }
1487 else
1488 myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
Bugfix45f88ce2017-03-06 14:41:56 +01001489 QueryProp(P_PRESAY):"") + capitalize(getuid(ME));
Zesstra8b5320e2022-02-18 21:10:26 +01001490 if (myname && sizeof(myname))
1491 myname=capitalize(myname);
1492
1493 // Wir haben Zielobjekt und unseren eigenen Namen. Erstmal an Empfaenger
1494 // senden.
1495 if (living(ob))
1496 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_ALERT, MA_TELL,
1497 myname + " teilt Dir mit: ");
1498 else
1499 _send_to_kobold(lname, permutate(msg), MT_COMM|MT_FAR|MSG_ALERT, MA_TELL,
1500 myname + " teilt Dir mit: ");
MG Mud User88f12472016-06-24 23:31:02 +02001501
1502 // dann evtl. noch an Absender ausgeben...
Zesstra8b5320e2022-02-18 21:10:26 +01001503 visflag = !ob->QueryProp(P_INVIS);
1504 if (visflag || IS_LEARNER(this_player()))
1505 _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(lname) + " mit: ");
MG Mud User88f12472016-06-24 23:31:02 +02001506 // oder irgendwas anderes an den Absender ausgeben...
1507 if (!visflag && interactive(ob))
Zesstra8b5320e2022-02-18 21:10:26 +01001508 ReceiveNotify("Kein solcher Spieler online!",MA_TELL);
Zesstra04f613c2019-11-27 23:32:54 +01001509 else if (away = ({string})ob->QueryProp(P_AWAY))
Zesstra8b5320e2022-02-18 21:10:26 +01001510 ReceiveMsg( break_string( away, 78, capitalize(lname)
MG Mud User88f12472016-06-24 23:31:02 +02001511 + " ist gerade nicht da: ", BS_INDENT_ONCE ),
1512 MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
1513 MA_TELL, 0, this_object());
1514 else if (interactive(ob) && (i=query_idle(ob))>=600)
1515 { //ab 10 Mins
1516 if (i<3600)
1517 away=time2string("%m %M",i);
1518 else
1519 away=time2string("%h %H und %m %M",i);
1520
Bugfixb1d9b4d2021-09-14 20:07:04 +02001521 ReceiveNotify(sprintf("%s ist seit %s voellig untaetig.",
Zesstra8b5320e2022-02-18 21:10:26 +01001522 capitalize(lname),away),
MG Mud User88f12472016-06-24 23:31:02 +02001523 MA_TELL);
1524 }
1525
1526 return 1;
1527}
1528
1529static int _teile(string str)
1530{
1531 string who, message;
1532 if (!(str=_unparsed_args())) return 0;
1533 if (sscanf(str, "%s mit %s", who, message) == 2)
1534 return _tell(who, message,1);
1535 return 0;
1536}
1537static int _teile_mit_alias(string str)
1538{
1539 str = _unparsed_args(), TRIM_LEFT;
1540 if (!str) return 0;
1541 str = trim(str, TRIM_LEFT);
1542 // Ziel muss min. 2 Buchstaben haben (.<nr>)
1543 if (sizeof(str) < 4) return 0;
1544 int pos = strstr(str, " ");
1545 if (pos >= 2)
1546 return _tell(str[..pos-1], str[pos+1..]);
1547 return 0;
1548}
1549
1550static int _erzaehle(string str)
1551{
1552 string who, message;
1553
1554 if (!(str=_unparsed_args())) return 0;
1555 if (sscanf(str, "%s %s", who, message) == 2)
1556 return _tell(who, message,1);
1557 return 0;
1558}
1559
1560static int _whisper(string str)
1561{
1562 object ob;
1563 string who;
1564 string msg;
1565 string myname;
1566
1567 if (!(str=_unparsed_args()) ||
1568 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1569 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001570 ReceiveNotify("Was willst Du wem zufluestern?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001571 return 1;
1572 }
Zesstra6e88b6a2019-11-08 00:25:39 +01001573 who = lower_case(who);
MG Mud User88f12472016-06-24 23:31:02 +02001574 if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001575 ReceiveNotify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001576 return 1;
1577 }
1578
1579 myname = capitalize((((IS_LEARNER(ME) &&
1580 !QueryProp(P_INVIS) &&
1581 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1582 QueryProp(P_PRESAY) : "") + name()) || "");
1583
1584 _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
Bugfixdbdbed52022-01-21 11:14:35 +01001585 MA_SAY, myname + " fluestert Dir zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001586 send_room(environment(),
1587 myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
1588 MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
1589
1590 _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
1591
1592
1593 return 1;
1594}
1595
1596static int _remote_whisper(string str)
1597{
1598 /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
1599 Nachricht nicht in der tell_history verewigt. */
1600
1601 object ob;
Zesstra8b5320e2022-02-18 21:10:26 +01001602 string who;
MG Mud User88f12472016-06-24 23:31:02 +02001603 string msg;
1604 string myname;
1605
1606 if (!(str=_unparsed_args()) ||
1607 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1608 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001609 ReceiveNotify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001610 return 1;
1611 }
1612
Zesstra8b5320e2022-02-18 21:10:26 +01001613 string|int lname = lower_case(who);
1614 if (!ob=find_player(lname))
MG Mud User88f12472016-06-24 23:31:02 +02001615 {
Zesstra8b5320e2022-02-18 21:10:26 +01001616 lname = match_living(lname, 0);
1617 if (!stringp(lname))
1618 {
1619 switch(lname)
1620 {
MG Mud User88f12472016-06-24 23:31:02 +02001621 case -1:
Zesstra8b5320e2022-02-18 21:10:26 +01001622 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001623 return 1;
1624 case -2:
Zesstra8b5320e2022-02-18 21:10:26 +01001625 // rfluester benutzt keinen KOBOLD.
1626 ReceiveNotify("Kein solcher Spieler!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001627 return 1;
1628 }
Zesstra8b5320e2022-02-18 21:10:26 +01001629 }
1630 // lname sollte nun eindeutig sein.
1631 ob = find_player(lname) || find_living(lname);
1632 if(!ob) {
1633 ReceiveNotify("Kein solcher Spieler online!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001634 return 1;
1635 }
1636 }
Zesstra8b5320e2022-02-18 21:10:26 +01001637
MG Mud User88f12472016-06-24 23:31:02 +02001638 if (environment(ob) == environment()) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001639 ReceiveNotify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001640 return 1;
1641 }
1642
1643 myname = capitalize((((IS_LEARNER(ME) &&
1644 !QueryProp(P_INVIS) &&
1645 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1646 QueryProp(P_PRESAY) : "") + name()) || "");
1647
1648 // An Empfaenger senden.
Zesstrabd1236a2022-02-09 22:35:59 +01001649 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE,
1650 MA_EMOTE, myname + " fluestert Dir aus der Ferne zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001651 // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
1652 if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
Zesstra8b5320e2022-02-18 21:10:26 +01001653 ReceiveNotify("Kein solcher Spieler online!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001654 return 1;
1655 }
1656 // sonst eigene Meldung via _recv() ausgeben.
1657 else
1658 _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
1659 "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
1660
1661 return 1;
1662}
1663
1664static int _converse(string arg)
1665{
Bugfixb1d9b4d2021-09-14 20:07:04 +02001666 ReceiveNotify("Mit '**' wird das Gespraech beendet.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001667 if (stringp(arg) && strstr(arg, "-s") == 0)
1668 input_to("_converse_more", INPUT_PROMPT, "]", 1);
1669 else
1670 input_to("_converse_more", INPUT_PROMPT, "]", 0);
1671 return 1;
1672}
1673
1674static int _converse_more(mixed str, int silent)
1675{
1676 if (str == "**") {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001677 ReceiveNotify("Ok.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001678 return 0;
1679 }
1680
1681 if(str != "")
1682 _communicate(str, silent);
1683
1684 input_to("_converse_more", INPUT_PROMPT, "]", silent);
1685 return 1;
1686}
1687
1688private int is_learner(object o) { return IS_LEARNER(o); }
1689
1690static int _shout_to_wizards(mixed str)
1691{
MG Mud User88f12472016-06-24 23:31:02 +02001692 string myname;
MG Mud User88f12472016-06-24 23:31:02 +02001693
1694 str = _unparsed_args();
1695 if (!str||!sizeof(str)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001696 ReceiveNotify("Was willst Du den Magiern zurufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001697 return 1;
1698 }
1699 // Kontrollzeichen rausfiltern.
1700 str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
1701 myname = capitalize(getuid(this_object()));
1702 if (!IS_LEARNER(this_object()))
1703 _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
1704
1705 // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
1706 filter(users(), #'is_learner)->ReceiveMsg(str,
Zesstrabd1236a2022-02-09 22:35:59 +01001707 MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_ALERT,
MG Mud User88f12472016-06-24 23:31:02 +02001708 MA_SHOUT, myname+" an alle Magier: ", this_object());
1709
1710 return 1;
1711}
1712
1713static int _echo(string str) {
1714 if (!IS_SEER(ME) || (!IS_LEARNER(ME)
1715 && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
1716 return 0;
1717
1718 if (!(str=_unparsed_args())) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001719 ReceiveNotify("Was moechtest Du 'echoen'?", 0);
MG Mud User88f12472016-06-24 23:31:02 +02001720 return 1;
1721 }
1722
1723 if (!IS_LEARNER(this_interactive()))
1724 {
1725 if (QueryProp(P_GHOST))
1726 {
1727 _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
1728 return 0;
1729 }
1730 if (QueryProp(P_SP)<ECHO_COST)
1731 {
1732 _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
1733 return 0;
1734 }
1735 SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
1736 str=">\b"+str;
1737 log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
1738 str));
1739 }
1740 // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
1741 // Kommhistory noch im Kobold landen.
1742 send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
1743 MA_UNKNOWN, 0, 0);
1744 return 1;
1745}
1746
1747// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
1748// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
1749// und gefedert.
1750static string *_set_ignore(mixed arg)
1751{
1752 raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
1753 "Benutze AddIgnore/RemoveIgnore()!\n");
1754}
1755// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
1756// nix dran, dass alle TestIgnore() & Co benutzen sollen.
1757static string *_query_ignore() {
1758 mixed ign=Query(P_IGNORE, F_VALUE);
1759 if (mappingp(ign))
1760 return m_indices(ign);
1761 return ({});
1762}
1763
1764public int AddIgnore(string ign) {
1765 // Einige strings sind nicht erlaubt, z.B. konsekutive .
1766 if (!sizeof(ign)
1767 || regmatch(ign,"[.]{2,}",RE_PCRE)
1768 || regmatch(ign," ",RE_PCRE)
1769 || sizeof(explode(ign,"."))>3)
1770 return 0;
1771
1772 mapping ignores=Query(P_IGNORE, F_VALUE);
1773 ignores[ign]=time();
1774 // kein Set() noetig.
1775 return 1;
1776}
1777
1778public int RemoveIgnore(string ign)
1779{
1780 mapping ignores=Query(P_IGNORE,F_VALUE);
1781 m_delete(ignores,ign);
1782 // Kein Set() noetig
1783 return 1;
1784}
1785
1786static int _query_intermud()
1787{
1788 mixed tmp;
1789 return member(pointerp(tmp=Query(P_CHANNELS))?tmp:({}), "Intermud") > -1;
1790}
1791
1792
1793int erwidere(string str)
1794{
1795 str=_unparsed_args();
1796 if (!str) return 0;
1797 return _tell(ERWIDER_PARAM, str ,1);
1798}
1799
1800static int tmhist(string str)
1801{
1802
1803 if (str == "aus") {
1804 tell_history_enabled = TELLHIST_DISABLED;
1805 write("Ok, es wird nichts mehr gespeichert.\n");
1806 if (sizeof(tell_history)) {
Zesstra996bafe2022-02-14 22:42:39 +01001807 clear_tell_history(1);
MG Mud User88f12472016-06-24 23:31:02 +02001808 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1809 }
1810 return 1;
1811 }
Zesstra996bafe2022-02-14 22:42:39 +01001812 if (str == "loeschen")
1813 {
1814 if (sizeof(tell_history)) {
1815 clear_tell_history(1);
1816 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1817 }
1818 else
1819 write("Deine Mitteilungsgeschichte war schon leer.\n");
1820 return 1;
1821 }
MG Mud User88f12472016-06-24 23:31:02 +02001822
1823 if (str == "namen") {
1824 int flag;
1825 tell_history_enabled = TELLHIST_NO_MESSAGE;
1826 write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
1827 foreach (string uid, struct chat_s chat: tell_history)
1828 if (pointerp(chat->msgbuf)) {
1829 chat->msgbuf = 0;
1830 chat->ptr = 0;
1831 flag = 1;
1832 }
1833 if (flag)
1834 write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
1835 return 1;
1836 }
1837
1838 if (str == "ein" || str == "an") {
1839 tell_history_enabled = TELLHIST_ENABLED;
1840 write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
1841 return 1;
1842 }
1843
MG Mud User88f12472016-06-24 23:31:02 +02001844 if (str == "langlebig") {
1845 tell_history_enabled = TELLHIST_LONGLIFE;
1846 write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
1847 "Ende/Crash/\nReboot gespeichert.\n");
1848 return 1;
1849 }
MG Mud User88f12472016-06-24 23:31:02 +02001850
1851 if (str == "status") {
1852 switch (tell_history_enabled) {
1853 case TELLHIST_DISABLED:
1854 write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
1855 break;
1856 case TELLHIST_NO_MESSAGE:
1857 write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
1858 break;
1859 case TELLHIST_ENABLED:
1860 write("Deine Mitteilungen werden gespeichert.\n");
1861 break;
MG Mud User88f12472016-06-24 23:31:02 +02001862 case TELLHIST_LONGLIFE:
1863 write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
1864 "Crash/Reboot\ngespeichert.\n");
1865 break;
MG Mud User88f12472016-06-24 23:31:02 +02001866 }
1867 return 1;
1868 }
1869
1870 if (tell_history_enabled == TELLHIST_DISABLED) {
1871 _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
1872 return 0;
1873 }
1874
1875 if (!sizeof(tell_history)) {
1876 _notify_fail("Du hast noch keinem etwas mitgeteilt "
1877 "und noch keine Mitteilungen erhalten.\n");
1878 return 0;
1879 }
1880
1881 if (str && sizeof(str)) {
1882
1883 if (tell_history_enabled < TELLHIST_ENABLED) {
1884 _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
1885 return 0;
1886 }
1887
1888 string uid;
1889 if (member(tell_history, str)) {
1890 // Name gewuenscht, da der String in der History vorkommt.
1891 uid = str;
1892 }
1893 else {
1894 // evtl. ne Zahl angegeben.
1895 int i;
1896 string *partners = sorted_commpartners(0);
1897 if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
1898 uid = partners[i];
1899 else {
1900 notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
1901 return 0;
1902 }
1903 }
1904
1905 mixed *data = tell_history[uid]->msgbuf;
1906 if (!data) {
1907 _notify_fail(
1908 "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
1909 return 0;
1910 }
1911
1912 int ptr = tell_history[uid]->ptr;
1913
1914 More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
1915 data[0..ptr-1],
Zesstra7459f252022-02-23 22:47:26 +01001916 function string (struct kobold_msg_s msg) {
MG Mud User88f12472016-06-24 23:31:02 +02001917 if (!structp(msg)) return "";
Zesstra3a261e52022-02-10 14:00:31 +01001918 return break_string(terminal_colour(msg->msg, colourmap)
Zesstra7459f252022-02-23 22:47:26 +01001919 + " ["
1920 + strftime("%H:%M:%S",msg->timestamp) + "]", 78,
MG Mud User88f12472016-06-24 23:31:02 +02001921 msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
1922 } ) ) );
1923 return 1;
1924 }
1925
1926 string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
1927 int i;
1928 foreach (string uid : sorted_commpartners(0) ) {
1929 int j;
1930 struct chat_s chat = tell_history[uid];
1931 history += sprintf("%2d.%-4s %s %-11s %d gesendet/%d empfangen\n", ++i,
1932 ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
1933 strftime("%a, %e.%m.%y",chat->time_last_msg),
1934 capitalize(chat->uid), chat->sentcount, chat->recvcount);
1935 }
1936
1937 More(history);
1938
1939 return 1;
1940}
1941
1942static mixed _query_localcmds()
1943{
1944 return ({
1945 ({"kobold", "cmd_kobold",0,0}),
1946 ({"sag","_communicate",0,0}),
1947 ({"sage","_communicate",0,0}),
1948 ({"'","_communicate",1,0}),
1949 ({"mruf","_shout_to_wizards",0,0}),
1950 ({"mrufe","_shout_to_wizards",0,0}),
1951 ({"ruf","_shout_to_all",0,0}),
1952 ({"rufe","_shout_to_all",0,0}),
1953 ({"erzaehl","_erzaehle",0,0}),
1954 ({"erzaehle","_erzaehle",0,0}),
1955 ({"teil","_teile",0,0}),
1956 ({"teile","_teile",0,0}),
1957 ({"tm","_teile_mit_alias",0,0}),
1958 ({"fluester","_whisper",0,0}),
1959 ({"fluestere","_whisper",0,0}),
1960 ({"rfluester","_remote_whisper",0,0}),
1961 ({"rfluestere","_remote_whisper",0,0}),
1962 ({"gespraech","_converse",0,0}),
1963 ({"echo","_echo",0,0}),
1964 ({"ignorier","ignoriere",0,0}),
1965 ({"ignoriere","ignoriere",0,0}),
1966 ({"tmhist","tmhist",0,0}),
1967 ({"erwider","erwidere",0,0}),
1968 ({"erwidere","erwidere",0,0}),
1969 ({"klingelton","_msg_beep",0,0}),
1970 ({"senderwiederholung","_msg_prepend",0,0}),
1971 ({"report","set_report",0,0}),
1972 })+channel::_query_localcmds();
1973}
1974
1975private string *sorted_commpartners(int reversed) {
1976 return sort_array(m_indices(tell_history),
1977 function int (string uid1, string uid2) {
1978 if (reversed)
1979 return tell_history[uid1]->time_last_msg >
1980 tell_history[uid2]->time_last_msg;
1981 else
1982 return tell_history[uid1]->time_last_msg <=
1983 tell_history[uid2]->time_last_msg;
1984 } );
1985}
1986
Zesstra0712bac2020-06-12 09:41:24 +02001987// Setzt den Prompt eines Interactives. Gerufen bei Objekterstellung,
1988// Reconnect und bei Magiern, wenn diese ihren Prompt oder ihr aktuelles
1989// Verzeichnis aendern.
MG Mud User88f12472016-06-24 23:31:02 +02001990static void modify_prompt() {
1991 string text = Query(P_PROMPT, F_VALUE);
1992
1993 if ( !stringp(text) || !sizeof(text) )
1994 text = "> ";
1995 else {
1996 string path = Query(P_CURRENTDIR, F_VALUE);
1997 if (stringp(path) && sizeof(path))
1998 text = regreplace(text,"\\w",path,0); // Pfad einsetzen
1999 }
2000 configure_interactive(this_object(), IC_PROMPT, text);
2001}
2002
2003// Prueft auf Ingoriereeintraege.
2004// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
MG Mud User88f12472016-06-24 23:31:02 +02002005public int TestIgnore(string|string* srcnames)
MG Mud User88f12472016-06-24 23:31:02 +02002006{
2007 mapping ign = Query(P_IGNORE, F_VALUE);
2008 if (stringp(srcnames))
2009 srcnames = ({srcnames});
2010
2011 foreach(string srcname: srcnames)
2012 {
2013 // einfachster Fall, exakter Match
2014 if (member(ign, srcname))
2015 return MSG_IGNORED;
2016 // ansonsten muss aufgetrennt werden.
2017 if (strstr(srcname,".") > -1)
2018 {
2019 string *srcparts=explode(srcname,".");
2020 switch(sizeof(srcparts))
2021 {
2022 // case 0 und 1 kann nicht passieren.
2023 case 3:
2024 // zu pruefen: [sender].aktion.qualifizierer.
2025 // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
2026 // oben schon geprueft. im Spieler geprueft werden muss noch:
2027 // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
2028 if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
2029 || member(ign, "."+srcparts[1]) // .aktion
2030 || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
2031 || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
2032 )
2033 {
2034 return MSG_IGNORED;
2035 }
2036 break;
2037 case 2:
2038 // zu pruefen: spieler.aktion
2039 // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
2040 // oben schon geprueft. Im Spieler zu pruefen ist noch:
2041 // spieler und .aktion
2042 if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
2043 || member(ign, "."+srcparts[1]))
2044 {
2045 return MSG_IGNORED;
2046 }
2047 break;
2048 default: // mehr als 3 Teile...
2049 raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
2050 "is supported. Got: %s\n",srcname));
MG Mud User88f12472016-06-24 23:31:02 +02002051 }
2052 }
2053 }
2054 // Default: nicht ignorieren.
2055 return 0;
2056}
2057
2058#ifdef __LPC_UNIONS__
2059public int TestIgnoreExt(string|string* srcnames)
2060#else
2061public int TestIgnoreExt(mixed srcnames)
2062#endif
2063{
2064 return TestIgnore(srcnames);
2065}
2066
2067// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
2068// Typen.
2069// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
2070private int check_ignores(string msg, int msg_type, string msg_action,
Zesstra7459f252022-02-23 22:47:26 +01002071 string msg_prefix, object|string origin)
MG Mud User88f12472016-06-24 23:31:02 +02002072{
2073 // Einige Dinge lassen sich nicht ignorieren.
2074 if (msg_type & (MT_NEWS|MT_NOTIFICATION))
2075 return 0;
2076 // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
2077 // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
2078 // eine ignorierbare msg_action geben.
2079 else if (stringp(msg_action) && origin && origin != ME)
2080 {
Zesstra7459f252022-02-23 22:47:26 +01002081 string srcname;
2082 if (objectp(origin))
2083 srcname =
MG Mud User88f12472016-06-24 23:31:02 +02002084 (query_once_interactive(origin) ? origin->query_real_name()
2085 : origin->name(WER) || "");
Zesstra7459f252022-02-23 22:47:26 +01002086 else
2087 srcname = origin;
2088
MG Mud User88f12472016-06-24 23:31:02 +02002089 mapping ign = Query(P_IGNORE, F_VALUE);
2090
2091 if (member(ign, srcname))
2092 return MSG_IGNORED;
2093 // vielleicht wird irgendwas a la name.aktion ignoriert?
2094 // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
2095 // Ebenenmeldungen)
2096 if (member(ign, srcname+"."+msg_action))
2097 return MSG_VERB_IGN;
2098 // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
2099 // das reichlich sinnfrei ist.
2100 if (member(ign, "."+msg_action))
2101 return MSG_VERB_IGN;
2102 // Spieler auf Ebenen ignoriert?
2103 // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
2104 if (strstr(msg_action, MA_CHANNEL) == 0)
2105 {
2106 // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
2107 if (member(ign, srcname + "."MA_CHANNEL))
2108 return MSG_IGNORED;
2109 // spieler.ebenen.ebenenname ist oben schon abgedeckt.
2110 // .ebenen halte ich fuer sinnfrei, nicht geprueft.
2111 }
2112 // Spieler aus anderem mud? *seufz*
2113 if (strstr(srcname,"@") > -1)
2114 {
2115 string *srcparts = explode(srcname,"@");
2116 if (sizeof(srcparts)==2)
2117 {
2118 // spieler@?
2119 if (member(ign, srcparts[0]+"@"))
2120 return MSG_IGNORED;
2121 // oder Mud per @mud?
2122 if (member(ign, "@" + srcparts[1]))
2123 return MSG_MUD_IGN;
2124 // BTW: spieler@mud wurde schon ganz oben erfasst.
2125 }
2126 }
2127 }
2128 // Default: nicht ignorieren.
2129 return 0;
2130}
2131
2132// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
2133// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
Bugfixe0fc68f2022-01-07 15:30:54 +01002134// BLindheit/Taubheit). In Zukunft koennten aber weitere Typen und weitere
2135// Kriterien wichtig werden und weitere Argumente uebergeben werden. (Dies
2136// bitte beachten, falls diese Funktion protected/public werden sollte.)
MG Mud User88f12472016-06-24 23:31:02 +02002137// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
2138// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
2139// Sinn gar nix zu tun hat.
2140// Rueckgabe: 0 oder MSG_SENSE_BLOCK
Bugfixe0fc68f2022-01-07 15:30:54 +01002141private int check_senses(int msg_type)
MG Mud User88f12472016-06-24 23:31:02 +02002142{
2143 int senses = msg_type & (MT_LOOK|MT_LISTEN);
2144 // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
2145 // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
2146 // unterdrueckt werden.
2147 if (!senses)
2148 return 0;
Bugfix427a5812022-01-07 15:41:24 +01002149
2150 int orig_senses = senses;
2151
MG Mud User88f12472016-06-24 23:31:02 +02002152 if ((senses & MT_LOOK) && CannotSee(1))
2153 senses &= ~MT_LOOK; // Sinn loeschen
2154
2155 if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
2156 senses &= ~MT_LISTEN;
2157
Bugfix427a5812022-01-07 15:41:24 +01002158 // Wenn kein Sinn mehr ueber ist oder all_types gesetzt ist und nicht mehr
2159 // alle Sinne vorhanden sind, wird die Nachricht nicht wahrgenommen.
2160 if (orig_senses == senses // kein Sinn geloescht, haeufigster Fall
2161 || (!(msg_type&MSG_ALL_SENSES) && senses) ) // min. ein Sinn uebrig
2162 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02002163
Bugfix427a5812022-01-07 15:41:24 +01002164 return MSG_SENSE_BLOCK;
MG Mud User88f12472016-06-24 23:31:02 +02002165}
2166
Zesstra09bcf042023-12-30 20:07:19 +01002167public int ReceiveMsg(string msg, int msg_type, string msg_action,
MG Mud User88f12472016-06-24 23:31:02 +02002168 string msg_prefix, object origin)
2169{
2170 if (!msg) return MSG_FAILED;
2171
2172 // Flags und Typen spalten
2173 int flags = msg_type & MSG_ALL_FLAGS;
2174 int type = msg_type & ~flags;
2175
Zesstra09bcf042023-12-30 20:07:19 +01002176 // msg_action kommt per Defaultargument, msg_type hat per Default 0, daher
2177 // ggf. auch noch den Typ raten (leider geht msg_type nicht als
2178 // Defaultargument, da es *nach* msg_action ausgewertet werden muss, aber in
2179 // der Argumentliste leider *vor* msg_action kommt...
MG Mud User88f12472016-06-24 23:31:02 +02002180 type ||= comm_guess_message_type(msg_action, origin);
2181
2182 // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
2183 if (msg_type & MT_DEBUG)
2184 {
2185 if (!QueryProp(P_WIZ_DEBUG)
2186 || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
2187 return MSG_FAILED;
2188 }
2189
2190 // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
2191 // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
2192 // Weiterbearbeitung.
2193 // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
2194 if (!(flags & MSG_DONT_IGNORE))
2195 {
Bugfix427a5812022-01-07 15:41:24 +01002196 // Sinne pruefen.
2197 int res=check_senses(msg_type);
MG Mud User88f12472016-06-24 23:31:02 +02002198 if (res) return res;
2199
2200 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
2201 res=check_ignores(msg, type, msg_action, msg_prefix, origin);
2202 if (res) return res;
2203 }
2204
Zesstra3a261e52022-02-10 14:00:31 +01002205 // Mentions in der Form @Charname werden in Kommunikation oder beim Hoeren
2206 // von Rufen und Sagen markiert und gegebenfalls gibt es ein Pieps dafuer
2207 int mention;
2208 if ((type & MT_COMM)
2209 || ((type & MT_LISTEN) && msg_action in ({MA_SAY, MA_SHOUT}))
2210 )
2211 {
2212 // Um den Charnamen Tags fuer terminal_colour() einfuegen.
2213 // Lookahead und Lookbehind assertions um die Whitespaces um das Wort
2214 // nicht in den Match einzuschliessen (und zu ersetzen).
2215 string tmp = regreplace(msg,
Zesstra88cf8172025-06-28 13:19:53 +02002216 sprintf("(?<=\\s){0,1}@%s(?=\\s*){0,1}",getuid(ME)),
Zesstra3a261e52022-02-10 14:00:31 +01002217 sprintf("%%^mention%%^@%s%%^normal%%^",
2218 capitalize(getuid(ME))),
2219 RE_PCRE|RE_GLOBAL|RE_CASELESS);
2220 send_debug(ME, tmp);
2221 // Der Vergleich ist weniger schlimm als es aussieht, weil die Strings
2222 // unterschiedlich gross sein werden und daher nicht zeichenweise
2223 // verglichen werden muessen. Es ist dann jedenfalls schneller als
2224 // getrennt mit regmatch oder strstr zu schauen, ob @charname
2225 // vorkommt.
2226 if (tmp != msg)
2227 {
2228 msg = tmp;
2229 mention = 1;
2230 }
2231 }
2232
MG Mud User88f12472016-06-24 23:31:02 +02002233 // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
2234 if ((type & MT_COMM))
2235 {
2236 // erstmal in der Komm-History ablegen, wenn gewuenscht.
2237 if ((!(flags & MSG_DONT_STORE)))
2238 {
2239 string uid;
2240 if (query_once_interactive(origin))
2241 uid = origin->query_real_name();
2242 else
2243 uid = origin->name(WER) || "<unbekannt>";
Zesstraa31cd5c2016-12-17 20:11:07 +01002244 add_to_tell_history(uid, 0, 1, msg, msg_prefix,
2245 (msg_action == MA_TELL ? MSGFLAG_TELL : 0 ) );
MG Mud User88f12472016-06-24 23:31:02 +02002246 }
2247
2248 // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
2249 // Ebenenmeldungen. (Die haben ggf. schon.)
2250 if (stringp(msg_action) && QueryProp(P_AWAY)
2251 && strstr(msg_action, MA_CHANNEL) != 0)
2252 {
2253 // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
2254 if (msg[<1] == '\n')
2255 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
2256 else
2257 msg = msg + " [" + strftime("%H:%M",time()) + "]";
2258 }
2259 // Kobold erlaubt und gewuenscht? Kobold ist fuer die
2260 // direkte Kommunikation mittels MT_COMM vorgesehen.
2261 // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
2262 // ignorieren.
2263 // die if-Konstruktion ist so, weil ich das _flush_cache() im else
2264 // brauche.
2265 if (query_editing(this_object()) || query_input_pending(this_object())
2266 || QueryProp(P_EARMUFFS))
2267 {
2268 if (!(flags & MSG_DONT_BUFFER)
Zesstra7459f252022-02-23 22:47:26 +01002269 && (QueryProp(P_BUFFER) & KOBOLD_ONLINE))
MG Mud User88f12472016-06-24 23:31:02 +02002270 {
2271 // Nachricht soll im Kobold gespeichert werden.
2272 return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
2273 }
2274 }
2275 else
2276 {
2277 // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
2278 // entleeren.
2279 _flush_cache(0);
2280 }
Bugfix3afcb792022-01-21 22:32:42 +01002281 }
2282
Zesstra3a261e52022-02-10 14:00:31 +01002283 // Farbtags ersetzen
2284 msg = terminal_colour(msg, colourmap);
2285
2286 // Alertton senden?
2287 if ((msg_type & MSG_ALERT) || mention)
2288 comm_beep(msg_action, mention);
MG Mud User88f12472016-06-24 23:31:02 +02002289
2290 // Ausgabenachricht bauen und an den Spieler senden.
2291 if (flags & MSG_DONT_WRAP)
2292 msg = (msg_prefix ? msg_prefix : "") + msg;
2293 else
2294 {
2295 int bsflags = flags & MSG_ALL_BS_FLAGS;
2296 if (QueryProp(P_MESSAGE_PREPEND))
2297 bsflags |= BS_PREPEND_INDENT;
2298 msg = break_string(msg, 78, msg_prefix, bsflags);
2299 }
2300 efun::tell_object(ME, msg);
2301
2302 return MSG_DELIVERED;
2303}
Zesstra7459f252022-02-23 22:47:26 +01002304
2305// called from base.c in Reconnect()
2306protected void reconnect() {
2307 // Cache fuer den report zuruecksetzen, der koennte veraltet sein (insb.
2308 // falls in der letzten Session GMCP benutzt wurde und jetzt nicht).
2309 report_cache = 0;
2310}
2311
2312protected void updates_after_restore(int newflag) {
2313 // Colourmap aktualisieren nach Restore
2314 colourmap = build_colourmap(QueryProp(P_TTY));
2315
2316 // Altes Ignoriere loeschen...
2317 mixed ign = Query(P_IGNORE,F_VALUE);
2318 if (!mappingp(ign))
2319 {
2320 if (pointerp(ign))
2321 ReceiveNotify(break_string(
2322 "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
2323 "weil es eine Aktualisierung der Ignorierefunktion gab, "
2324 "bei der eine Konversion der Daten leider nicht "
2325 "moeglich war.",78), 0);
2326
2327 Set(P_IGNORE, ([]), F_VALUE);
2328 }
2329 // ggf. Comm-Vault abrufen oder neu erstellen.
2330 setup_comm_vault();
2331 // Wenn es eins gibt, den Inhalt zu unserem internen Koboldpuffer
2332 // hinzufuegen, von wo es spaeter angezeigt wird.
2333 if (commvault)
2334 process_comm_vault(commvault);
Bugfix895dc452025-06-30 17:35:45 +02002335
2336 // P_ALERT korrigieren
2337 // Das historische Originalverhalten war, bei P_ALERT == 0 alle Klingeltöne
2338 // abzuspielen. Benachrichtigungen für Kommunikation wurden jedoch nur
2339 // abgespielt, wenn P_MESSAGE_BEEP zusaetzlich auf einen Wert > 0 gesetzt war.
2340 // Diesem Verhalten kommt P_ALERT == MB_MISC am naechsten.
2341 if(!QueryProp(P_ALERT))
2342 {
2343 SetProp(P_ALERT, MB_MISC);
2344 }
Zesstra7459f252022-02-23 22:47:26 +01002345}
2346