blob: c32f590e54065599523d550beceaa24ed5a5a7a1 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// player/comm.c-- basic player communiction commands
4//
5// $Id: comm.c 9576 2016-06-18 15:00:01Z Zesstra $
6#pragma strong_types
7#pragma save_types
8#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +02009//#pragma range_check
10
11inherit "/std/living/comm";
12inherit "/std/player/channel";
13inherit "/std/player/comm_structs";
14
15#include <input_to.h>
16
17#define NEED_PROTOTYPES
18#include <player/quest.h>
19#include <player/gmcp.h>
20#include <living/description.h>
21#undef NEED_PROTOTYPES
22
23#include <sys_debug.h>
24
25#include <thing/properties.h>
26#include <player/comm.h>
27#include <player/base.h>
28
29#include <properties.h>
30#include <config.h>
31#include <ansi.h>
32#include <wizlevels.h>
33#include <language.h>
34#include <udp.h>
35#include <defines.h>
36#include <daemon.h>
37#include <strings.h>
38#include <regexp.h>
39#include <interactive_info.h>
40
41#define TELLHIST_DISABLED 0
42#define TELLHIST_NO_MESSAGE 1
43#define TELLHIST_ENABLED 2
44#define TELLHIST_LONGLIFE 3
45
46#define ECHO_COST 50
47#define ERWIDER_PARAM ","
48
49#define ZDEBUG(x) if (find_player("zesstra"))\
50 efun::tell_object(find_player("zesstra"),"CommDBG: "+x+"\n")
51
52private int tell_history_enabled = TELLHIST_NO_MESSAGE;
53private nosave mapping tell_history=([]);
54private nosave string *commreceivers = ({});
55private nosave string last_comm_partner;
56private nosave int last_beep_time;
57
58// Statusreporte aktiviert? Binaere Flags (s. set_report())
59private int stat_reports;
60// interner Cache fuer die LP/KP/Gift-Werte fuer die Statusreport-Ausgaben
61// Eintraege (in dieser Reihenfolge): P_HP, P_SP, Giftstatus
62// Initialisierung erfolgt beim ersten Report nach Login
63private nosave mixed *report_cache;
64
65// Puffer fuer Kobold.
66private nosave struct msg_buffer_s kobold = (<msg_buffer_s>
67 buf: allocate(32),
68 index: -1,);
69#define MAX_KOBOLD_LIMIT 256
70
71varargs string name(int casus, int demonst);
72
73//local property prototypes
74static int _query_intermud();
75public int RemoveIgnore(string ign);
76public int AddIgnore(string ign);
77
78public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
79 string msg_prefix, object origin);
80
81// erzeugt sortierte Liste an Kommunikationspartnern
82private string *sorted_commpartners(int reversed);
83
MG Mud User88f12472016-06-24 23:31:02 +020084void create()
85{
86 ::create();
87 Set(P_EARMUFFS, 0);
88 Set(P_EARMUFFS, SAVE, F_MODE);
89 Set(P_EARMUFFS, SECURED, F_MODE);
90 Set(P_INTERMUD, SAVE, F_MODE);
91 Set(P_IGNORE, ([]), F_VALUE);
92 Set(P_IGNORE, SAVE, F_MODE);
93 Set(P_BUFFER, SAVE, F_MODE);
94 Set(P_MESSAGE_PREPEND, SAVE, F_MODE_AS);
95 Set(P_MESSAGE_BEEP, SAVE, F_MODE_AS);
96}
97
98void create_super()
99{
100 set_next_reset(-1);
101}
102
Zesstra0681c732020-10-31 19:45:47 +0100103// called from base.c in Reconnect()
104protected void reconnect() {
105 // Cache fuer den report zuruecksetzen, der koennte veraltet sein (insb.
106 // falls in der letzten Session GMCP benutzt wurde und jetzt nicht).
107 report_cache = 0;
108}
109
MG Mud User88f12472016-06-24 23:31:02 +0200110// uebermittelt eine MT_NOTIFICATION an this_object(), welche nicht ignoriert
111// werden kann und auch nicht gespeichert wird.
112protected void _notify(string msg, string action) {
113 ReceiveMsg(msg,
114 MT_NOTIFICATION|MSG_DONT_BUFFER|MSG_DONT_IGNORE|MSG_DONT_STORE,
115 action, 0, this_object());
116}
117
118protected void updates_after_restore(int newflag) {
119 // Altes Ignoriere loeschen...
120 mixed ign = Query(P_IGNORE,F_VALUE);
121 if (!mappingp(ign))
122 {
123 if (pointerp(ign))
124 _notify(break_string(
125 "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
126 "weil es eine Aktualisierung der Ignorierefunktion gab, "
127 "bei der eine Konversion der Daten leider nicht "
128 "moeglich war.",78), 0);
129
130 Set(P_IGNORE, ([]), F_VALUE);
131 }
132}
133
Zesstra2504b2d2020-05-22 12:30:17 +0200134static int set_report(string str)
135{
MG Mud User88f12472016-06-24 23:31:02 +0200136 int canflags = QueryProp(P_CAN_FLAGS);
MG Mud User88f12472016-06-24 23:31:02 +0200137 if(!str)
138 {
Zesstracf8f2952020-05-22 12:07:52 +0200139 if (stat_reports)
140 {
141 string *res=({});
142 if (stat_reports & DO_REPORT_HP)
143 res+=({"Lebenspunkte"});
144 if (stat_reports & DO_REPORT_SP)
145 res+=({"Konzentrationspunkte"});
146 if (stat_reports & DO_REPORT_POISON)
147 res+=({"Vergiftungen"});
148 if (stat_reports & DO_REPORT_WIMPY)
149 res+=({"Vorsicht"});
MG Mud User88f12472016-06-24 23:31:02 +0200150
Zesstracf8f2952020-05-22 12:07:52 +0200151 tell_object(ME,break_string(
MG Mud User88f12472016-06-24 23:31:02 +0200152 "Dir werden jetzt Veraenderungen Deiner "
153 +CountUp(res) + " berichtet.",78));
Zesstra86ec63b2020-05-22 12:09:56 +0200154 if (GMCP_Status("MG.char") || GMCP_Status("char")
155 || GMCP_Status("Char"))
156 {
157 tell_object(ME,break_string(
158 "Achtung: Dein Client laesst sich den Report per GMCP "
Zesstra2504b2d2020-05-22 12:30:17 +0200159 "(s. 'hilfe GMCP') uebermitteln. Daher wird er Dir nicht "
Zesstra86ec63b2020-05-22 12:09:56 +0200160 "in der Textausgabe des Spiels angezeigt! Moechtest Du "
161 "dies nicht, schalte bitte in Deinem Client GMCP-Module mit "
162 "Namen wie 'MG.char', 'char', 'Char' oder aehnliche aus."));
163 }
MG Mud User88f12472016-06-24 23:31:02 +0200164 }
165 else
166 tell_object(ME,
167 "Alle Statusreports sind ausgeschaltet.\n");
168
169 return 1;
170 }
Zesstra2504b2d2020-05-22 12:30:17 +0200171 else if (str == "aus")
172 {
173 if (stat_reports & DO_REPORT_HP || stat_reports & DO_REPORT_WIMPY)
174 {
MG Mud User88f12472016-06-24 23:31:02 +0200175 string s="";
Zesstra2504b2d2020-05-22 12:30:17 +0200176 if (stat_reports & DO_REPORT_HP)
177 {
MG Mud User88f12472016-06-24 23:31:02 +0200178 str="ebenfalls ";
179 tell_object(ME, "Der Report wurde ausgeschaltet.\n");
180 }
Zesstra2504b2d2020-05-22 12:30:17 +0200181 if ( stat_reports & DO_REPORT_WIMPY )
182 {
MG Mud User88f12472016-06-24 23:31:02 +0200183 tell_object(ME, "Der Vorsicht-Report wurde "+s+
184 "ausgeschaltet.\n");
185 }
186 stat_reports=0;
187 }
Zesstra2504b2d2020-05-22 12:30:17 +0200188 else
189 {
MG Mud User88f12472016-06-24 23:31:02 +0200190 tell_object(ME, "Der Report ist bereits ausgeschaltet.\n");
191 }
192 return 1;
193 }
Zesstra2504b2d2020-05-22 12:30:17 +0200194 else if (str == "ein")
195 {
196 if ( stat_reports & DO_REPORT_HP )
197 {
MG Mud User88f12472016-06-24 23:31:02 +0200198 tell_object(ME, "Der Report ist bereits eingeschaltet.\n");
199 return 1;
200 }
201 tell_object(ME, "Der Report wurde eingeschaltet.\n");
202 stat_reports |= DO_REPORT_HP;
Zesstra2504b2d2020-05-22 12:30:17 +0200203 if (!(canflags & CAN_REPORT_SP))
204 {
205 if (QueryQuest("Hilf den Gnarfen")==1)
206 {
MG Mud User88f12472016-06-24 23:31:02 +0200207 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_SP);
208 stat_reports |= DO_REPORT_SP;
209 }
Zesstra2504b2d2020-05-22 12:30:17 +0200210 else
211 {
MG Mud User88f12472016-06-24 23:31:02 +0200212 tell_object(ME, break_string(
213 "Fuer den Statusreport Deiner Konzentration musst Du jedoch "
214 "zunaechst die Quest \"Hilf den Gnarfen\" bestehen.",78));
215 }
216 }
Zesstra2504b2d2020-05-22 12:30:17 +0200217 else
218 {
MG Mud User88f12472016-06-24 23:31:02 +0200219 stat_reports |= DO_REPORT_SP;
220 }
Zesstra2504b2d2020-05-22 12:30:17 +0200221 if (!(canflags & CAN_REPORT_POISON))
222 {
223 if (QueryQuest("Katzenjammer")==1)
224 {
MG Mud User88f12472016-06-24 23:31:02 +0200225 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_POISON);
226 stat_reports |= DO_REPORT_POISON;
227 }
Zesstra2504b2d2020-05-22 12:30:17 +0200228 else
229 {
MG Mud User88f12472016-06-24 23:31:02 +0200230 tell_object(ME, break_string(
231 "Fuer den Statusreport Deiner Vergiftung musst Du jedoch "
232 "zunaechst die Quest \"Katzenjammer\" bestehen.",78));
233 }
234 }
Zesstra2504b2d2020-05-22 12:30:17 +0200235 else
236 {
MG Mud User88f12472016-06-24 23:31:02 +0200237 stat_reports |= DO_REPORT_POISON;
238 }
239 // Cache loeschen, damit beim naechsten Report-Event alle Daten neu
240 // eingetragen werden muessen. Muss beim Einschalten des Reports
241 // passieren, weil auch in der inaktiven Zeit weiterhin Aenderungen in
242 // status_report() eingehen, so dass der Cache zwar erst einmal leer ist,
243 // aber beim Wiedereinschalten nicht mehr ungueltig waere und somit
244 // veraltete Daten an den Spieler ausgegeben werden. Im unguenstigsten
245 // Fall wuerde das sogar dazu fuehren, dass die veralteten Daten lange
246 // Zeit nicht aktualisiert werden, wenn z.B. P_HP == P_MAX_HP, so dass
247 // kein P_HP-Event mehr eingeht.
248 report_cache=0;
Zesstra2504b2d2020-05-22 12:30:17 +0200249 // Fall-through fuer Statusausgabe
MG Mud User88f12472016-06-24 23:31:02 +0200250 }
Zesstra2504b2d2020-05-22 12:30:17 +0200251 else if (str == "vorsicht")
252 {
253 if (!(canflags & CAN_REPORT_WIMPY))
254 {
255 if (QueryQuest("Schrat kann nicht einschlafen")==1)
256 {
MG Mud User88f12472016-06-24 23:31:02 +0200257 SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_WIMPY);
258 tell_object(ME, "Der Vorsicht-Report wurde eingeschaltet.\n");
259 stat_reports |= DO_REPORT_WIMPY;
260 }
Zesstra2504b2d2020-05-22 12:30:17 +0200261 else
262 {
MG Mud User88f12472016-06-24 23:31:02 +0200263 tell_object(ME, break_string(
264 "Fuer den Statusreport Deiner Vorsicht musst Du "
265 "zunaechst die Quest \"Schrat kann nicht einschlafen\" "
266 "bestehen.",78));
267 }
268 }
269 else
270 {
271 stat_reports |= DO_REPORT_WIMPY;
272 }
273 // fuer Seher auch Bericht der Fluchtrichtung einschalten.
274 if ((stat_reports & DO_REPORT_WIMPY)
275 && !(stat_reports & DO_REPORT_WIMPY_DIR)
276 && ((canflags & CAN_REPORT_WIMPY) || IS_SEER(ME)))
277 {
Zesstra2504b2d2020-05-22 12:30:17 +0200278 stat_reports |= DO_REPORT_WIMPY_DIR;
MG Mud User88f12472016-06-24 23:31:02 +0200279 }
Zesstra2504b2d2020-05-22 12:30:17 +0200280 // Fall-through fuer Statusausgabe
MG Mud User88f12472016-06-24 23:31:02 +0200281 }
282 // sendet einmalig genau jetzt den konfigurierten report. Kann zum testen
283 // (von Triggern) oder beim Login benutzt werden, wenn man einen initialen
284 // Datenbestand erhalten will.
285 else if (str=="senden")
286 {
287 // Es wird Ausgabe von LP und Vorsicht getriggert, das sendet beide
288 // Zeilen.
289 status_report(DO_REPORT_HP, QueryProp(P_HP));
290 status_report(DO_REPORT_WIMPY, QueryProp(P_WIMPY));
291 return 1;
292 }
293 else
294 return 0;
295 // nur aktuellen Zustand berichten
296 set_report(0);
297 return 1;
298}
299
300private string get_poison_desc(int p) {
301 string ret;
302 if ( intp(p) ) {
303 switch(p) {
304 case 0: ret="keins"; break;
305 case 1..3: ret="leicht"; break;
306 case 4..8: ret="gefaehrlich"; break;
307 default: ret="sehr ernst"; break;
308 }
309 return ret;
310 }
311 else return "(nicht verfuegbar)";
312}
313
314// sprintf()-Formatstrings fuer die Reportausgabe.
315#define REPORTLINE "LP: %3d, KP: %3s, Gift: %s.\n"
316#define REPORTLINE_WIMPY "Vorsicht: %d, Fluchtrichtung: %s.\n"
317// Defines zur Adressierung der Cache-Eintraege
318#define REP_HP 0
319#define REP_SP 1
320#define REP_POISON 2
321
322protected void status_report(int type, mixed val) {
323 // Wenn der Spieler GMCP hat und das sich um die Information kuemmert,
324 // erfolgt keine textuelle Ausgabe mehr. Daher return, wenn GMCP_Char()
325 // erfolg vermeldet hat.
326 int flags = QueryProp(P_CAN_FLAGS);
327 switch (type) {
328 case DO_REPORT_HP:
329 if (GMCP_Char( ([ P_HP: val ]) ) ) return;
330 break;
331 case DO_REPORT_SP:
332 if (!(flags & CAN_REPORT_SP)) return;
333 if (GMCP_Char( ([ P_SP: val ]) ) ) return;
334 break;
335 case DO_REPORT_POISON:
336 if (!(flags & CAN_REPORT_POISON)) return;
337 if (GMCP_Char( ([ P_POISON: val ]) ) ) return;
338 break;
339 case DO_REPORT_WIMPY:
340 if (!(flags & CAN_REPORT_WIMPY)) return;
341 if (GMCP_Char( ([ P_WIMPY: val ]) ) ) return;
342 break;
343 case DO_REPORT_WIMPY_DIR:
344 if (!(flags & CAN_REPORT_WIMPY_DIR)) return;
345 if (GMCP_Char( ([ P_WIMPY_DIRECTION: val ]) ) ) return;
346 break;
347 }
348
349 // konventionelle textuelle Ausgabe des Reports ab hier.
350 if (!(type & stat_reports))
351 return;
352
353 if ( !report_cache ) {
354 report_cache = ({
355 QueryProp(P_HP),
356 (stat_reports&DO_REPORT_SP) ? to_string(QueryProp(P_SP)) : "###",
357 (stat_reports&DO_REPORT_POISON) ?
358 get_poison_desc(QueryProp(P_POISON)) : "(nicht verfuegbar)"
359 });
360 }
361
362 switch(type) {
363 // LP berichten: Cache aktualisieren und Meldung ausgeben.
364 case DO_REPORT_HP:
365 report_cache[REP_HP]=val;
366 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
367 report_cache[REP_SP], report_cache[REP_POISON]));
368 break;
369 // KP berichten: Wenn der Spieler den Report freigeschaltet hat,
370 // wird bei Aenderungen gemeldet. Wenn nicht, aendert sich nur der
371 // Cache-Eintrag. So wird verhindert, dass ein Spieler ueber KP-
372 // Veraenderungen auch dann informiert wuerde, wenn er den KP-Report
373 // gar nicht benutzen koennte.
374 case DO_REPORT_SP:
375 report_cache[REP_SP]=to_string(val);
376 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
377 report_cache[REP_SP], report_cache[REP_POISON]));
378 break;
379 // Giftstatus berichten: Wenn der Giftreport freigeschaltet ist,
380 // Cache aktualisieren und berichten. Wenn nicht, aendert sich nur
381 // der Cache-Eintrag. Erlaeuterung hierzu s.o. beim KP-Report.
382 case DO_REPORT_POISON:
383 report_cache[REP_POISON] = get_poison_desc(val);
384 tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
385 report_cache[REP_SP], report_cache[REP_POISON]));
386 break;
387 // Vorsicht-Report: kann ohne weitere Abfragen ausgegeben werden, da
388 // alle noetigen Checks schon zu Beginn dieser Funktion erledigt wurden.
389 // Lediglich der Inhalt der Meldung muss abhaengig vom Seherstatus
390 // konfiguriert werden.
391 case DO_REPORT_WIMPY:
392 string res;
393 if (IS_SEER(ME)) {
394 // QueryProp() aus Kostengruenden im if(), damit die Aufruf-
395 // Haeufigkeit zumindest ein wenig reduziert wird.
396 string dir = QueryProp(P_WIMPY_DIRECTION)||"keine";
397 res = sprintf(REPORTLINE_WIMPY, val, dir);
398 }
399 else
400 res = sprintf(REPORTLINE_WIMPY, val, "(nicht verfuegbar)");
401 tell_object(ME, res);
402 break;
403 // Fluchtrichtungs-Report: wird nur bei Sehern ausgegeben, damit
404 // nicht auch Spieler eine VS-/FR-Meldung bekommen, wenn z.B. eine
405 // externe Manipulation der Fluchtrichtung stattfindet, sie aber den
406 // Report mangels Seherstatus gar nicht freigeschaltet haben.
407 case DO_REPORT_WIMPY_DIR:
408 if (IS_SEER(ME)) {
409 if (!val) val = "keine";
410 tell_object(ME,sprintf(REPORTLINE_WIMPY, QueryProp(P_WIMPY), val));
411 }
412 break;
413 }
414}
415
416#undef REPORTLINE
417#undef REPORTLINE_WIMPY
418#undef REP_HP
419#undef REP_SP
420#undef REP_POISON
421
422private string permutate(string msg)
423{
424 // Kontrollzeichen rausfiltern. *seufz*
425 msg = regreplace(msg,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
426 object ob=QueryProp(P_PERM_STRING);
427 if (!objectp(ob))
428 return msg;
429
Zesstra04f613c2019-11-27 23:32:54 +0100430 return ({string})ob->permutate_string(msg)||"";
MG Mud User88f12472016-06-24 23:31:02 +0200431}
432
433// neue nachricht an den Kobold anhaengen
434// Rueckgabewerte: MSG_BUFFER_FULL oder MSG_BUFFERED
435private int add_to_kobold(string msg, int msg_type, string msg_action,
436 string msg_prefix, object origin)
437{
438 // Nachricht soll im Kobold gespeichert werden.
439 // Kobold speichert Rohdaten und gibt spaeter das ganze auch wieder via
440 // ReceiveMsg() aus - dabei wird MSG_DONT_BUFFER | MSG_DONT_STORE gesetz,
441 // damit keine erneute Speicher in Kobold oder Komm-History erfolgt.
442
443 // wenn der Puffer zu klein ist, Groesse verdoppeln, wenn noch unterhalb
444 // des Limits.
445 if (kobold->index >= sizeof(kobold->buf)-1) {
446 if (sizeof(kobold->buf) < MAX_KOBOLD_LIMIT)
447 kobold->buf += allocate(sizeof(kobold->buf));
448 else
449 return MSG_BUFFER_FULL;
450 }
451 kobold->index = kobold->index +1;
452 // neue Nachricht an den Puffer anhaengen.
453 string sendername = query_once_interactive(origin) ?
454 origin->query_real_name() :
455 origin->name(WER) || "<Unbekannt>";
456 kobold->buf[kobold->index] = (<msg_s> msg: msg,
457 type : msg_type, action : msg_action, prefix : msg_prefix,
458 sendername : sendername);
459 return MSG_BUFFERED;
460}
461
462private void _flush_cache(int verbose) {
463 // nur mit genug Evalticks ausgeben.
464 if (get_eval_cost() < 100000) return;
465 if (kobold->index >= 0)
466 {
467 ReceiveMsg("Ein kleiner Kobold teilt Dir folgendes mit:",
468 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
469 0, 0, this_object());
470 int prepend = QueryProp(P_MESSAGE_PREPEND);
471 foreach(int i: 0 .. kobold->index) // '0 ..' ist wichtig!
472 {
473 struct msg_s msg = kobold->buf[i];
474 // dies ist dient der Fehlerabsicherung, falls es nen Fehler (z.B. TLE)
475 // in der Schleife unten gab: dann ist index nicht auf -1 gesetzt
476 // worden, aber einige Nachrichten sind schon geloescht.
477 if (!structp(msg)) continue;
478 // Ausgabe via efun::tell_object(), weil die Arbeit von ReceiveMsg()
479 // schon getan wurde. Allerdings muessen wir uns noch um den UMbruch
480 // kuemmern.
481 if ((msg->type) & MSG_DONT_WRAP)
482 msg->msg = (msg->prefix ? msg->prefix : "") + msg->msg;
483 else
484 {
485 int bsflags = msg->type & MSG_ALL_BS_FLAGS;
486 if (prepend)
487 bsflags |= BS_PREPEND_INDENT;
488 msg->msg = break_string(msg->msg, 78, msg->prefix, bsflags);
489 }
490 efun::tell_object(this_object(), msg->msg);
491 kobold->buf[i]=0;
492 }
493 kobold->index=-1;
494 }
495 else if (verbose)
496 {
497 ReceiveMsg("Der kleine Kobold hat leider nichts Neues fuer Dich.",
498 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
499 0, 0, this_object());
500 }
501}
502
503varargs int cmd_kobold(string arg)
504{
505 switch(arg)
506 {
507 case "ein":
508 SetProp(P_BUFFER, 1);
509 printf("Der Kobold merkt sich jetzt alles!\n"); break;
510 case "aus":
511 SetProp(P_BUFFER, 0);
512 printf("Der Kobold wird Dich nicht stoeren!\n"); break;
513 default: if(arg) printf("Der Kobold sagt: kobold ein oder kobold aus\n");
514 }
515 _flush_cache(1);
516 return 1;
517}
518
519public int TestIgnoreSimple(string *arg)
Zesstrade642d22019-11-23 17:22:34 +0100520{ mapping ignore;
MG Mud User88f12472016-06-24 23:31:02 +0200521
522 if (!pointerp(arg) || !mappingp(ignore=Query(P_IGNORE,F_VALUE)))
523 return 0;
524 foreach(string s: arg)
525 {
526 if (member(ignore,s))
527 return 1;
528 }
529 return 0;
530}
531
532//TODO: deprecated - entfernen, wenn Message() entfernt wird.
533private int check_ignore(mixed ignore, string verb, string name)
534{
535 if (ignore == verb)
536 return 1;
537 ignore = explode(ignore, ".");
538 return ((sizeof(ignore) > 1) &&
539 (name == ignore[0] && member(ignore[1..], verb) != -1));
540}
541
542private int comm_beep() {
543 if (QueryProp(P_VISUALBELL)) return 0; // kein ton
Zesstra04f613c2019-11-27 23:32:54 +0100544 int beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
MG Mud User88f12472016-06-24 23:31:02 +0200545 if (!beep_interval || ((time()-last_beep_time) < beep_interval)) return 0;
546 last_beep_time=time();
547 return 1;
548}
549
550private varargs void add_to_tell_history( string uid, int sent, int recv,
551 string message, string indent, int flags )
552{
553 /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
554 Als Wert ist eine Strukur vom Typ chat_s eingetragen.
555 Strukturen chat_s und stored_msg_s sind in /std/player/comm_structs.c
556 definiert.
557 TODO fuer spaeter, gerade keine Zeit fuer:
558 Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
559 jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
560 weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
561 Element ist aeltestes Gespraech).
562 */
563
564 //TODO: Entfernen, wenn das nicht mehr passiert.
565 if (!stringp(uid))
566 {
567 ReceiveMsg(sprintf(
568 "\nadd_to_tell_history(): got bad uid argument %O."
569 "sent: %d, recv: %d, flags: %d, msg: %s",
570 uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);
571 }
Zesstraa31cd5c2016-12-17 20:11:07 +0100572
MG Mud User88f12472016-06-24 23:31:02 +0200573 // letzten Gespraechspartner fuer erwidere.
Zesstraa31cd5c2016-12-17 20:11:07 +0100574 if (flags & (MSGFLAG_TELL|MSGFLAG_RTELL))
MG Mud User88f12472016-06-24 23:31:02 +0200575 last_comm_partner = uid;
576
577 // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
578 // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
579 // Anfang.
580 if (sent) {
581 if (!sizeof(commreceivers))
582 commreceivers = ({uid});
583 else if (commreceivers[0] != uid) {
584 // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
585 // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
586 // entfernen.
587 // TODO: Effizienter gestalten.
588 commreceivers = ({uid}) + (commreceivers-({uid}));
589 // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
590 // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
591 // commreceivers drin sind.)
592 if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
593 commreceivers = commreceivers[0..MAX_SAVED_CHATS];
594 }
595 }
596
597 if (!tell_history_enabled)
598 return;
599
600 if (!indent && message[<1] == 10)
601 message = message[..<2];
602
603 struct chat_s chat;
604 // Gespraechspartner unbekannt?
605 if (!member(tell_history, uid)) {
606 // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
607 // dazu kommt.
608 if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
609 string deluid;
610 int zeit = __INT_MAX__;
611 foreach(string tuid, chat : tell_history) {
612 // aeltestes Gespraech suchen
613 if (zeit > chat->time_last_msg) {
614 deluid = tuid;
615 zeit = chat->time_last_msg;
616 }
617 }
618 // aeltestes Gespraech raus.
619 m_delete(tell_history, deluid);
620 if (member(commreceivers,deluid)>-1)
621 commreceivers-=({deluid});
622 }
623 // neues Gespraech anlegen
624 chat = (<chat_s> uid: uid, time_first_msg: time(),
625 time_last_msg: time(),
626 sentcount: sent, recvcount: recv,
627 msgbuf: 0, ptr: 0 );
628 tell_history[uid] = chat;
629 }
630 else {
631 // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
632 chat = tell_history[uid];
633 chat->time_last_msg = time();
634 chat->sentcount += sent;
635 chat->recvcount += recv;
636 }
637
638 if (tell_history_enabled < TELLHIST_ENABLED)
639 return;
640
641 // ggf. Array fuer Messages anlegen
642 if (!pointerp(chat->msgbuf))
643 chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
644
645 // Message-Struktur ermitteln oder neu anlegen
646 struct stored_msg_s msg;
647 if (!structp(chat->msgbuf[chat->ptr])) {
648 // neue Struct ins Array schreiben
649 chat->msgbuf[chat->ptr] = msg = (<stored_msg_s>);
650 }
651 else {
652 // alte Struct ueberschreiben
653 msg = chat->msgbuf[chat->ptr];
654 }
655 // Index auf naechste Messagestruktur ermitteln
656 chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
657 // Message speichern
658 msg->msg = message;
659 msg->prefix = indent;
660 msg->timestamp = time();
661}
662
663protected void clear_tell_history()
664{
665 /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
666 sofern der Spieler nichts abweichendes eingestellt hat. */
667
668#ifdef TELLHIST_LONGLIFE
669 if (tell_history_enabled == TELLHIST_LONGLIFE)
670 return;
671#endif
672
673 foreach (string uid, struct chat_s chat: tell_history)
674 if (pointerp(chat->msgbuf)) {
675 chat->msgbuf = 0;
676 chat->ptr = 0;
677 }
678}
679
680protected void reset(void)
681{
682 /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
683 Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
684 wie bei einem "schlafe ein" die Mitteilungen geloescht. */
685
686 if (!interactive())
687 clear_tell_history();
688
689}
690
691// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
692// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
693// werden soll.
694private void _send(object ob, string msg, int msg_type,
695 string msg_action, string msg_prefix)
696{
697 int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
698 switch(res) {
699 case MSG_DELIVERED:
700 break; // nix machen
701 case MSG_BUFFERED:
Zesstra62b4a862022-12-23 20:08:16 +0100702 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200703 "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
704 "genommen. Er wird sie spaeter weiterleiten!",
705 MT_NOTIFICATION, msg_action, 0, this_object());
706 break;
707 case MSG_IGNORED:
708 case MSG_VERB_IGN:
709 case MSG_MUD_IGN:
710 ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
711 MT_NOTIFICATION, msg_action, 0, this_object());
712 break;
713 case MSG_SENSE_BLOCK:
714 ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
715 MT_NOTIFICATION, msg_action, 0, this_object());
716 break;
717 case MSG_BUFFER_FULL:
Zesstra62b4a862022-12-23 20:08:16 +0100718 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200719 "Die Mitteilung ging verloren, denn der Kobold kann sich "
720 "nichts mehr merken!", MT_NOTIFICATION, msg_action,
721 0, this_object());
722 break;
723 default:
724 ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
725 "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
726 break;
727 }
728}
729
730// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
731// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
732// die Nachricht ggf. in den Kommhistory erfasst wird.
733// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
734protected varargs int _recv(object ob, string message, int flag, string indent)
735{
736 write(break_string(message, 78, indent,
737 QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
738 if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
739 query_once_interactive(ob))
740 {
741 if (flag & MSGFLAG_WHISPER)
742 add_to_tell_history(getuid(ob), 1, 0,
743 "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
744 flag);
745 else
746 add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
747 }
748 return 1;
749}
750
751// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
752// Fehlerbehandlung) ist es jedoch auch mal ein String.
753varargs int Message(string msg, int flag, string indent,
754 string cname, mixed sender)
755{
756 object ti;
757 string verb, reply, *ignore, tin;
758 int em, te;
759 mixed deaf;
760
761 // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
762 // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
763 // damit 'ignoriere name.debug' funktioniert.
764 if( flag == MSGFLAG_CHANNEL ){
765 if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
766 || msg[1..9]=="Warnungen"))
767 {
768 // Missbrauch der Variable 'ignore' als Zwischenspeicher
769 ignore = regexplode( msg, ":| |\\]" );
770 verb = lower_case(ignore[0][1..]);
771 tin = lower_case(ignore[2]);
772 }
773 else
774 {
775 if(cname)
776 verb=lower_case(cname);
777 else
778 verb=query_verb();
779 if( ti = this_interactive() )
780 {
781 tin = getuid(this_interactive());
782 }
783 else
784 {
785 //falls doch kein Objekt...
786 if (objectp(sender))
787 tin=lower_case(sender->name(RAW)||"<Unbekannt>");
788 }
789 }
790 }
791 else {
792 if( ti = this_interactive() )
793 tin = getuid(this_interactive());
794 verb = query_verb();
795 }
796
797 te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
798
799 // fuer "erwidere"
800 if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
801 if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
802 if (flag & MSGFLAG_WHISPER)
803 add_to_tell_history(getuid(ti), 0, 1,
804 capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
805 (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
806 ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
807 " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
808 else
809 add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
810 }
811 }
812 // Hoert der Spieler nicht?
813 em = (ti &&
814 (te || flag & MSGFLAG_SHOUT) &&
815 (QueryProp(P_EARMUFFS) &&
816 (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
817 ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
818
819 // Werden der Sender oder das Verb ignoriert?
820 if(!ti && tin && flag == MSGFLAG_CHANNEL)
821 {
822 if((member(ignore, tin) != -1))
823 {
824 return MESSAGE_IGNORE_YOU;
825 }
826 if(verb && sizeof(filter(ignore, #'check_ignore, verb, tin)) )
827 {
828 return MESSAGE_IGNORE_YOU;
829 }
830 }
831 if (ti && (member(ignore, getuid(ti)) != -1)) {
832 if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
833 efun::tell_object(ti, capitalize(name())+
834 " hoert gar nicht zu, was Du sagst.\n");
835 return MESSAGE_IGNORE_YOU;
836 }
837 if(tin && verb &&
838 sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
839 {
840 if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
841 verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
842 efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
843 return MESSAGE_IGNORE_VERB;
844 }
845 if (flag & MSGFLAG_RTELL) {
846 int at;
847
848 verb = lower_case(old_explode(msg, " ")[0][1..]);
849 at = member(verb, '@');
850 /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
851 add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
852
853 if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
854 return MESSAGE_IGNORE_YOU;
855 else if (at > 0 && member(ignore, verb[at..]) >= 0)
856 return MESSAGE_IGNORE_MUD;
857 }
858
859 // Taubheit/Oropax
860 te |= (flag & MSGFLAG_SAY);
861
862 if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
863 deaf = QueryProp(P_DEAF);
864 if (te)
865 reply = stringp(deaf) ?
866 capitalize(sprintf(deaf, name())) :
867 capitalize(name())+" ist momentan leider taub.\n";
868 }
869 else if (em)
870 reply = capitalize(name())+" hat Oropax in den Ohren.\n";
871
872 msg = break_string(msg, 78, indent,
873 (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
874
875 if(QueryProp(P_BUFFER) &&
876 (deaf ||
877 query_editing(this_object()) ||
878 query_input_pending(this_object())))
879 {
880 deaf = MESSAGE_DEAF;
881 if(flag & MSGFLAG_CACHE)
882 {
883 if(!stringp(reply))
884 reply = name()+" moechte gerade nicht gestoert werden.\n";
885
886 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
887
888 int res = add_to_kobold(msg, 0, 0, 0,
889 objectp(sender) ? sender : ME);
890 if(res == MSG_BUFFERED)
891 {
892
893 reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
894 "genommen.\nEr wird sie spaeter weiterleiten!";
895 deaf = MESSAGE_CACHE;
896 }
897 else {
898 reply += "Die Mitteilung ging verloren, denn "+
899 "der Kobold kann sich nichts mehr merken!";
900 deaf = MESSAGE_CACHE_FULL;
901 }
902 if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
903 efun::tell_object(ti, reply+"\n");
904 }
905 return deaf;
906 }
907 else if((deaf || em) &&
908 ( (flag & MSGFLAG_RTELL) ||
909 (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
910 if (te && ti)
911 efun::tell_object(ti, reply);
912 return MESSAGE_DEAF;
913 }
914
915 _flush_cache(0);
916 if(te && QueryProp(P_AWAY))
917 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
918
919 if (flag & (MSGFLAG_SAY | MSGFLAG_TELL) && comm_beep()) {
920 msg=MESSAGE_BEEP+msg;
921 }
922 efun::tell_object(ME, msg);
923 return MESSAGE_OK;
924}
925
926static int ignoriere(string str)
927{
928 str = _unparsed_args(1);
929 mapping ignore=Query(P_IGNORE, F_VALUE);
930
931 if (!str)
932 {
933 string* ignarr = m_indices(ignore);
934 if (!sizeof(ignarr))
935 tell_object(ME, "Du ignorierst niemanden.\n");
936 else
937 ReceiveMsg("Du ignorierst:\n"
938 + break_string(CountUp(map(sort_array(ignarr, #'> ),
939 #'capitalize )
940 ) + ".",78),
941 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
942 0,0,this_object());
943 return 1;
944 }
945 // trim spaces from args and convert to lower case.
946 str = lower_case(trim(str, TRIM_BOTH));
947
948 if (member(ignore, str))
949 {
950 RemoveIgnore(str);
951 tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
952 }
953 else if (sizeof(ignore)>100)
954 {
955 tell_object(ME, "Du ignorierst schon genuegend!\n");
956 }
957 else if (AddIgnore(str) == 1)
958 {
959 tell_object(ME,
960 sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
961 }
962 else
963 {
964 tell_object(ME,
965 sprintf("'%s' kannst Du nicht ignorieren.\n",str));
966 }
967 return 1;
968}
969
970
971static int _msg_beep(string str) {
972 int beep_interval;
973 notify_fail("Syntax: klingelton <1 bis 3600 Sekunden> oder klingelton aus\n");
974 if (stringp(str)) {
975 if (str=="aus")
976 SetProp(P_MESSAGE_BEEP,0);
977 else if ((beep_interval=to_int(str)) > 0 && beep_interval<=3600)
978 SetProp(P_MESSAGE_BEEP,beep_interval);
979 else return 0;
980 }
981
Zesstra593b2c72019-11-27 23:37:03 +0100982 beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
MG Mud User88f12472016-06-24 23:31:02 +0200983 _notify("Ton bei Mitteilungen: "+
984 (beep_interval ? "aller "+beep_interval+" Sekunden." : "aus."),
985 query_verb());
986 return 1;
987}
988
989static int _msg_prepend(string str) {
MG Mud User88f12472016-06-24 23:31:02 +0200990 notify_fail("Syntax: senderwiederholung ein/aus\n");
991 if (stringp(str)) {
992 if (str=="aus")
993 SetProp(P_MESSAGE_PREPEND,1);
994 else if (str=="ein")
995 SetProp(P_MESSAGE_PREPEND,0);
996 else return 0;
997 }
998
999 _notify("Senderwiederholung bei Mitteilungen: "+
Zesstra04f613c2019-11-27 23:32:54 +01001000 (({int})QueryProp(P_MESSAGE_PREPEND) ? "aus" : "ein")+".",
MG Mud User88f12472016-06-24 23:31:02 +02001001 query_verb());
1002
1003 return 1;
1004}
1005
1006static int _communicate(mixed str, int silent)
1007{
1008 string verb;
1009 string myname;
1010 string msg;
1011
1012 if (!str || extern_call()) str=_unparsed_args()||"";
1013 /* str=_unparsed_args()||""; */
1014 verb = query_verb();
1015 if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
1016 if (str==""||str==" "||!str)
1017 {
1018 _notify("Was willst Du sagen?",MA_SAY);
1019 return 1;
1020 }
1021 msg=permutate(str);
1022
1023 myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
1024 !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
1025 "":QueryProp(P_PRESAY))+name())||"";
1026
1027 // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
1028 // ist, keine MT_COMM)
1029 send_room(environment(), msg, MT_LISTEN, MA_SAY,
1030 capitalize(myname)+" sagt: ", ({this_object()}) );
1031
1032 if(!silent)
1033 {
1034 ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1035 MA_SAY, "Du sagst: ", ME);
1036 }
1037 return 1;
1038}
1039
1040static int _shout_to_all(mixed str)
1041{
1042 string pre, myname, realname, wizards_msg, players_msg;
1043 string wizard_prefix, player_prefix;
1044 int chars;
1045
1046 if (!(str=_unparsed_args()))
1047 {
1048 _notify("Was willst Du rufen?",MA_SHOUT);
1049 return 1;
1050 }
1051 chars=sizeof(str)/2;
1052 if (chars<4) chars=4;
1053 pre = (!IS_LEARNER(ME) ||
1054 QueryProp(P_INVIS) ||
1055 !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
1056 realname = capitalize((pre + capitalize(getuid()))||"");
1057 myname = capitalize(pre + name()||"");
1058 if (QueryProp(P_INVIS))
1059 realname = "("+realname+")";
1060
1061 wizards_msg = permutate(str);
1062 wizard_prefix = myname+" ruft: ";
1063
1064 if(QueryProp(P_FROG)) {
1065 players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
1066 player_prefix = myname+" quakt: ";
1067 }
1068 else {
1069 players_msg = wizards_msg;
1070 player_prefix = wizard_prefix;
1071 }
1072
1073 if(!IS_LEARNER(this_player()))
1074 {
1075 if(QueryProp(P_GHOST)) {
1076 _notify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
1077 MA_SHOUT);
1078 return 1;
1079 }
1080 if (QueryProp(P_SP) <(chars+20))
1081 {
1082 _notify("Du musst erst wieder magische Kraefte sammeln.",
1083 MA_SHOUT);
1084 _notify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
1085 "Ebenen').", MA_SHOUT);
1086 return 1;
1087 }
1088 SetProp(P_SP, QueryProp(P_SP) - chars - 20);
1089 }
1090
1091 ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1092 "rufe", "Du rufst: ", ME);
1093
1094 foreach ( object ob : users()-({this_object()}) )
1095 if ( IS_LEARNER(ob) )
1096 ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR, MA_SHOUT, wizard_prefix,
1097 this_object());
1098 else
1099 ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR, MA_SHOUT, player_prefix,
1100 this_object());
1101
1102 return 1;
1103}
1104
1105varargs int _tell(string who, mixed msg)
1106{
1107 object ob;
1108 string away,myname,ret;
Arathornb3051452021-05-13 21:13:03 +02001109 mixed it;
MG Mud User88f12472016-06-24 23:31:02 +02001110 string *xname;
1111 int i,visflag;
1112
1113 if (extern_call() && this_interactive()!=ME) return 1;
1114 if (!who || !msg) {
1115 _notify("Was willst Du mitteilen?",MA_TELL);
1116 return 1;
1117 }
1118
1119 if(who == ERWIDER_PARAM)
1120 {
1121 if (!last_comm_partner)
1122 {
1123 _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
1124 "Du was erwidern\nkoenntest.\n");
1125 return 0;
1126 }
1127 who=last_comm_partner;
1128 }
1129
1130 // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
1131 if (who == ".")
1132 who = ".1";
1133
1134 if ( sscanf(who, ".%d", i) == 1 ) {
1135 if(i > 0 && i <= sizeof(commreceivers))
1136 who = commreceivers[i-1];
1137 else {
1138 _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
1139 return 0;
1140 }
1141 }
1142
1143 xname = explode(who, "@");
1144
1145 if (sizeof(xname) == 2)
1146 {
1147 if ( QueryProp(P_QP) )
1148 {
Zesstra04f613c2019-11-27 23:32:54 +01001149 if (ret=({string})INETD->_send_udp(xname[1],
MG Mud User88f12472016-06-24 23:31:02 +02001150 ([ REQUEST: "tell",
1151 RECIPIENT: xname[0],
1152 SENDER: getuid(ME),
1153 DATA: msg ]), 1))
1154 {
1155 _notify(ret, MA_TELL);
1156 }
1157 else
1158 {
1159 write("Nachricht abgeschickt.\n");
1160 add_to_tell_history(who, 1, 0, msg,
Zesstraa31cd5c2016-12-17 20:11:07 +01001161 "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_RTELL, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001162 }
1163 }
1164 else
1165 write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
1166 "Muds etwas mitteilen zu koennen.\n");
1167 return 1;
1168 }
1169
1170 if (!ob=find_player(it = lower_case(who)))
1171 {
1172 it = match_living(it, 0);
1173 if (!stringp(it))
1174 switch(it) {
1175 case -1:
1176 _notify("Das war nicht eindeutig!",MA_TELL);
1177 return 1;
1178 case -2:
1179 _notify("Kein solcher Spieler!",MA_TELL);
1180 return 1;
1181 }
1182 ob = find_player(it) || find_living(it);
1183 if(!ob) {
1184 _notify("Kein solcher Spieler!",MA_TELL);
1185 return 1;
1186 }
1187 }
1188
1189 if(QueryProp(P_INVIS)){
1190 if(!IS_LEARNER(ob))
1191 myname = name();
1192 else
1193 myname="("+
1194 ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
1195 capitalize(getuid()) + ")";
1196 }
1197 else
1198 myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
Bugfix45f88ce2017-03-06 14:41:56 +01001199 QueryProp(P_PRESAY):"") + capitalize(getuid(ME));
MG Mud User88f12472016-06-24 23:31:02 +02001200 if (myname && sizeof(myname)) myname=capitalize(myname);
1201 // erstmal an Empfaenger senden
1202 _send(ob, permutate(msg), MT_COMM|MT_FAR, MA_TELL,
1203 myname + " teilt Dir mit: ");
1204
1205 // dann evtl. noch an Absender ausgeben...
1206 if (visflag = !ob->QueryProp(P_INVIS) || IS_LEARNER(this_player()))
1207 _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(it) + " mit: ");
1208 // oder irgendwas anderes an den Absender ausgeben...
1209 if (!visflag && interactive(ob))
1210 _notify("Kein solcher Spieler!",MA_TELL);
Zesstra04f613c2019-11-27 23:32:54 +01001211 else if (away = ({string})ob->QueryProp(P_AWAY))
MG Mud User88f12472016-06-24 23:31:02 +02001212 ReceiveMsg( break_string( away, 78, capitalize(it)
1213 + " ist gerade nicht da: ", BS_INDENT_ONCE ),
1214 MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
1215 MA_TELL, 0, this_object());
1216 else if (interactive(ob) && (i=query_idle(ob))>=600)
1217 { //ab 10 Mins
1218 if (i<3600)
1219 away=time2string("%m %M",i);
1220 else
1221 away=time2string("%h %H und %m %M",i);
1222
1223 _notify(sprintf("%s ist seit %s voellig untaetig.",
1224 capitalize(it),away),
1225 MA_TELL);
1226 }
1227
1228 return 1;
1229}
1230
1231static int _teile(string str)
1232{
1233 string who, message;
1234 if (!(str=_unparsed_args())) return 0;
1235 if (sscanf(str, "%s mit %s", who, message) == 2)
1236 return _tell(who, message,1);
1237 return 0;
1238}
1239static int _teile_mit_alias(string str)
1240{
1241 str = _unparsed_args(), TRIM_LEFT;
1242 if (!str) return 0;
1243 str = trim(str, TRIM_LEFT);
1244 // Ziel muss min. 2 Buchstaben haben (.<nr>)
1245 if (sizeof(str) < 4) return 0;
1246 int pos = strstr(str, " ");
1247 if (pos >= 2)
1248 return _tell(str[..pos-1], str[pos+1..]);
1249 return 0;
1250}
1251
1252static int _erzaehle(string str)
1253{
1254 string who, message;
1255
1256 if (!(str=_unparsed_args())) return 0;
1257 if (sscanf(str, "%s %s", who, message) == 2)
1258 return _tell(who, message,1);
1259 return 0;
1260}
1261
1262static int _whisper(string str)
1263{
1264 object ob;
1265 string who;
1266 string msg;
1267 string myname;
1268
1269 if (!(str=_unparsed_args()) ||
1270 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1271 sscanf(str, "%s %s", who, msg) !=2 )) {
1272 _notify("Was willst Du wem zufluestern?",MA_SAY);
1273 return 1;
1274 }
Zesstra6e88b6a2019-11-08 00:25:39 +01001275 who = lower_case(who);
MG Mud User88f12472016-06-24 23:31:02 +02001276 if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
1277 _notify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
1278 return 1;
1279 }
1280
1281 myname = capitalize((((IS_LEARNER(ME) &&
1282 !QueryProp(P_INVIS) &&
1283 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1284 QueryProp(P_PRESAY) : "") + name()) || "");
1285
1286 _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
Bugfixdbdbed52022-01-21 11:14:35 +01001287 MA_SAY, myname + " fluestert Dir zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001288 send_room(environment(),
1289 myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
1290 MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
1291
1292 _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
1293
1294
1295 return 1;
1296}
1297
1298static int _remote_whisper(string str)
1299{
1300 /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
1301 Nachricht nicht in der tell_history verewigt. */
1302
1303 object ob;
1304 string who, it;
1305 string msg;
1306 string myname;
1307
1308 if (!(str=_unparsed_args()) ||
1309 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1310 sscanf(str, "%s %s", who, msg) !=2 )) {
1311 _notify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
1312 return 1;
1313 }
1314
1315 if (!ob=find_player(it = lower_case(who)))
1316 {
1317 it = match_living(it, 0);
1318 if (!stringp(it))
1319 switch(it){
1320 case -1:
1321 _notify("Das war nicht eindeutig!",MA_EMOTE);
1322 return 1;
1323 case -2:
1324 _notify("Kein solcher Spieler!",MA_EMOTE);
1325 return 1;
1326 }
1327 ob = find_player(it);
1328 if(!ob) ob = find_living(it);
1329 if(!ob){
1330 _notify("Kein solcher Spieler!",MA_EMOTE);
1331 return 1;
1332 }
1333 }
1334 if (environment(ob) == environment()) {
1335 _notify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
1336 return 1;
1337 }
1338
1339 myname = capitalize((((IS_LEARNER(ME) &&
1340 !QueryProp(P_INVIS) &&
1341 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1342 QueryProp(P_PRESAY) : "") + name()) || "");
1343
1344 // An Empfaenger senden.
1345 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE, MA_EMOTE,
1346 myname + " fluestert Dir aus der Ferne zu: ");
1347
1348 // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
1349 if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
1350 _notify("Kein solcher Spieler!",MA_EMOTE);
1351 return 1;
1352 }
1353 // sonst eigene Meldung via _recv() ausgeben.
1354 else
1355 _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
1356 "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
1357
1358 return 1;
1359}
1360
1361static int _converse(string arg)
1362{
1363 _notify("Mit '**' wird das Gespraech beendet.",MA_SAY);
1364 if (stringp(arg) && strstr(arg, "-s") == 0)
1365 input_to("_converse_more", INPUT_PROMPT, "]", 1);
1366 else
1367 input_to("_converse_more", INPUT_PROMPT, "]", 0);
1368 return 1;
1369}
1370
1371static int _converse_more(mixed str, int silent)
1372{
1373 if (str == "**") {
1374 _notify("Ok.",MA_SAY);
1375 return 0;
1376 }
1377
1378 if(str != "")
1379 _communicate(str, silent);
1380
1381 input_to("_converse_more", INPUT_PROMPT, "]", silent);
1382 return 1;
1383}
1384
1385private int is_learner(object o) { return IS_LEARNER(o); }
1386
1387static int _shout_to_wizards(mixed str)
1388{
MG Mud User88f12472016-06-24 23:31:02 +02001389 string myname;
MG Mud User88f12472016-06-24 23:31:02 +02001390
1391 str = _unparsed_args();
1392 if (!str||!sizeof(str)) {
1393 _notify("Was willst Du den Magiern zurufen?",MA_SHOUT);
1394 return 1;
1395 }
1396 // Kontrollzeichen rausfiltern.
1397 str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
1398 myname = capitalize(getuid(this_object()));
1399 if (!IS_LEARNER(this_object()))
1400 _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
1401
1402 // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
1403 filter(users(), #'is_learner)->ReceiveMsg(str,
1404 MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE,
1405 MA_SHOUT, myname+" an alle Magier: ", this_object());
1406
1407 return 1;
1408}
1409
1410static int _echo(string str) {
1411 if (!IS_SEER(ME) || (!IS_LEARNER(ME)
1412 && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
1413 return 0;
1414
1415 if (!(str=_unparsed_args())) {
1416 _notify("Was moechtest Du 'echoen'?", 0);
1417 return 1;
1418 }
1419
1420 if (!IS_LEARNER(this_interactive()))
1421 {
1422 if (QueryProp(P_GHOST))
1423 {
1424 _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
1425 return 0;
1426 }
1427 if (QueryProp(P_SP)<ECHO_COST)
1428 {
1429 _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
1430 return 0;
1431 }
1432 SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
1433 str=">\b"+str;
1434 log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
1435 str));
1436 }
1437 // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
1438 // Kommhistory noch im Kobold landen.
1439 send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
1440 MA_UNKNOWN, 0, 0);
1441 return 1;
1442}
1443
1444// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
1445// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
1446// und gefedert.
1447static string *_set_ignore(mixed arg)
1448{
1449 raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
1450 "Benutze AddIgnore/RemoveIgnore()!\n");
1451}
1452// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
1453// nix dran, dass alle TestIgnore() & Co benutzen sollen.
1454static string *_query_ignore() {
1455 mixed ign=Query(P_IGNORE, F_VALUE);
1456 if (mappingp(ign))
1457 return m_indices(ign);
1458 return ({});
1459}
1460
1461public int AddIgnore(string ign) {
1462 // Einige strings sind nicht erlaubt, z.B. konsekutive .
1463 if (!sizeof(ign)
1464 || regmatch(ign,"[.]{2,}",RE_PCRE)
1465 || regmatch(ign," ",RE_PCRE)
1466 || sizeof(explode(ign,"."))>3)
1467 return 0;
1468
1469 mapping ignores=Query(P_IGNORE, F_VALUE);
1470 ignores[ign]=time();
1471 // kein Set() noetig.
1472 return 1;
1473}
1474
1475public int RemoveIgnore(string ign)
1476{
1477 mapping ignores=Query(P_IGNORE,F_VALUE);
1478 m_delete(ignores,ign);
1479 // Kein Set() noetig
1480 return 1;
1481}
1482
1483static int _query_intermud()
1484{
1485 mixed tmp;
1486 return member(pointerp(tmp=Query(P_CHANNELS))?tmp:({}), "Intermud") > -1;
1487}
1488
1489
1490int erwidere(string str)
1491{
1492 str=_unparsed_args();
1493 if (!str) return 0;
1494 return _tell(ERWIDER_PARAM, str ,1);
1495}
1496
1497static int tmhist(string str)
1498{
1499
1500 if (str == "aus") {
1501 tell_history_enabled = TELLHIST_DISABLED;
1502 write("Ok, es wird nichts mehr gespeichert.\n");
1503 if (sizeof(tell_history)) {
1504 tell_history = ([]);
1505 commreceivers = ({});
1506 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1507 }
1508 return 1;
1509 }
1510
1511 if (str == "namen") {
1512 int flag;
1513 tell_history_enabled = TELLHIST_NO_MESSAGE;
1514 write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
1515 foreach (string uid, struct chat_s chat: tell_history)
1516 if (pointerp(chat->msgbuf)) {
1517 chat->msgbuf = 0;
1518 chat->ptr = 0;
1519 flag = 1;
1520 }
1521 if (flag)
1522 write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
1523 return 1;
1524 }
1525
1526 if (str == "ein" || str == "an") {
1527 tell_history_enabled = TELLHIST_ENABLED;
1528 write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
1529 return 1;
1530 }
1531
1532#ifdef TELLHIST_LONGLIFE
1533 if (str == "langlebig") {
1534 tell_history_enabled = TELLHIST_LONGLIFE;
1535 write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
1536 "Ende/Crash/\nReboot gespeichert.\n");
1537 return 1;
1538 }
1539#endif
1540
1541 if (str == "status") {
1542 switch (tell_history_enabled) {
1543 case TELLHIST_DISABLED:
1544 write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
1545 break;
1546 case TELLHIST_NO_MESSAGE:
1547 write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
1548 break;
1549 case TELLHIST_ENABLED:
1550 write("Deine Mitteilungen werden gespeichert.\n");
1551 break;
1552#ifdef TELLHIST_LONGLIFE
1553 case TELLHIST_LONGLIFE:
1554 write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
1555 "Crash/Reboot\ngespeichert.\n");
1556 break;
1557#endif
1558 }
1559 return 1;
1560 }
1561
1562 if (tell_history_enabled == TELLHIST_DISABLED) {
1563 _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
1564 return 0;
1565 }
1566
1567 if (!sizeof(tell_history)) {
1568 _notify_fail("Du hast noch keinem etwas mitgeteilt "
1569 "und noch keine Mitteilungen erhalten.\n");
1570 return 0;
1571 }
1572
1573 if (str && sizeof(str)) {
1574
1575 if (tell_history_enabled < TELLHIST_ENABLED) {
1576 _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
1577 return 0;
1578 }
1579
1580 string uid;
1581 if (member(tell_history, str)) {
1582 // Name gewuenscht, da der String in der History vorkommt.
1583 uid = str;
1584 }
1585 else {
1586 // evtl. ne Zahl angegeben.
1587 int i;
1588 string *partners = sorted_commpartners(0);
1589 if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
1590 uid = partners[i];
1591 else {
1592 notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
1593 return 0;
1594 }
1595 }
1596
1597 mixed *data = tell_history[uid]->msgbuf;
1598 if (!data) {
1599 _notify_fail(
1600 "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
1601 return 0;
1602 }
1603
1604 int ptr = tell_history[uid]->ptr;
1605
1606 More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
1607 data[0..ptr-1],
1608 function string (struct stored_msg_s msg) {
1609 if (!structp(msg)) return "";
1610 return break_string( msg->msg + " <"
1611 + strftime("%H:%M:%S",msg->timestamp) + ">", 78,
1612 msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
1613 } ) ) );
1614 return 1;
1615 }
1616
1617 string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
1618 int i;
1619 foreach (string uid : sorted_commpartners(0) ) {
1620 int j;
1621 struct chat_s chat = tell_history[uid];
1622 history += sprintf("%2d.%-4s %s %-11s %d gesendet/%d empfangen\n", ++i,
1623 ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
1624 strftime("%a, %e.%m.%y",chat->time_last_msg),
1625 capitalize(chat->uid), chat->sentcount, chat->recvcount);
1626 }
1627
1628 More(history);
1629
1630 return 1;
1631}
1632
1633static mixed _query_localcmds()
1634{
1635 return ({
1636 ({"kobold", "cmd_kobold",0,0}),
1637 ({"sag","_communicate",0,0}),
1638 ({"sage","_communicate",0,0}),
1639 ({"'","_communicate",1,0}),
1640 ({"mruf","_shout_to_wizards",0,0}),
1641 ({"mrufe","_shout_to_wizards",0,0}),
1642 ({"ruf","_shout_to_all",0,0}),
1643 ({"rufe","_shout_to_all",0,0}),
1644 ({"erzaehl","_erzaehle",0,0}),
1645 ({"erzaehle","_erzaehle",0,0}),
1646 ({"teil","_teile",0,0}),
1647 ({"teile","_teile",0,0}),
1648 ({"tm","_teile_mit_alias",0,0}),
1649 ({"fluester","_whisper",0,0}),
1650 ({"fluestere","_whisper",0,0}),
1651 ({"rfluester","_remote_whisper",0,0}),
1652 ({"rfluestere","_remote_whisper",0,0}),
1653 ({"gespraech","_converse",0,0}),
1654 ({"echo","_echo",0,0}),
1655 ({"ignorier","ignoriere",0,0}),
1656 ({"ignoriere","ignoriere",0,0}),
1657 ({"tmhist","tmhist",0,0}),
1658 ({"erwider","erwidere",0,0}),
1659 ({"erwidere","erwidere",0,0}),
1660 ({"klingelton","_msg_beep",0,0}),
1661 ({"senderwiederholung","_msg_prepend",0,0}),
1662 ({"report","set_report",0,0}),
1663 })+channel::_query_localcmds();
1664}
1665
1666private string *sorted_commpartners(int reversed) {
1667 return sort_array(m_indices(tell_history),
1668 function int (string uid1, string uid2) {
1669 if (reversed)
1670 return tell_history[uid1]->time_last_msg >
1671 tell_history[uid2]->time_last_msg;
1672 else
1673 return tell_history[uid1]->time_last_msg <=
1674 tell_history[uid2]->time_last_msg;
1675 } );
1676}
1677
Zesstra0712bac2020-06-12 09:41:24 +02001678// Setzt den Prompt eines Interactives. Gerufen bei Objekterstellung,
1679// Reconnect und bei Magiern, wenn diese ihren Prompt oder ihr aktuelles
1680// Verzeichnis aendern.
MG Mud User88f12472016-06-24 23:31:02 +02001681static void modify_prompt() {
1682 string text = Query(P_PROMPT, F_VALUE);
1683
1684 if ( !stringp(text) || !sizeof(text) )
1685 text = "> ";
1686 else {
1687 string path = Query(P_CURRENTDIR, F_VALUE);
1688 if (stringp(path) && sizeof(path))
1689 text = regreplace(text,"\\w",path,0); // Pfad einsetzen
1690 }
1691 configure_interactive(this_object(), IC_PROMPT, text);
1692}
1693
1694// Prueft auf Ingoriereeintraege.
1695// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
MG Mud User88f12472016-06-24 23:31:02 +02001696public int TestIgnore(string|string* srcnames)
MG Mud User88f12472016-06-24 23:31:02 +02001697{
1698 mapping ign = Query(P_IGNORE, F_VALUE);
1699 if (stringp(srcnames))
1700 srcnames = ({srcnames});
1701
1702 foreach(string srcname: srcnames)
1703 {
1704 // einfachster Fall, exakter Match
1705 if (member(ign, srcname))
1706 return MSG_IGNORED;
1707 // ansonsten muss aufgetrennt werden.
1708 if (strstr(srcname,".") > -1)
1709 {
1710 string *srcparts=explode(srcname,".");
1711 switch(sizeof(srcparts))
1712 {
1713 // case 0 und 1 kann nicht passieren.
1714 case 3:
1715 // zu pruefen: [sender].aktion.qualifizierer.
1716 // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
1717 // oben schon geprueft. im Spieler geprueft werden muss noch:
1718 // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
1719 if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
1720 || member(ign, "."+srcparts[1]) // .aktion
1721 || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
1722 || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
1723 )
1724 {
1725 return MSG_IGNORED;
1726 }
1727 break;
1728 case 2:
1729 // zu pruefen: spieler.aktion
1730 // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
1731 // oben schon geprueft. Im Spieler zu pruefen ist noch:
1732 // spieler und .aktion
1733 if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
1734 || member(ign, "."+srcparts[1]))
1735 {
1736 return MSG_IGNORED;
1737 }
1738 break;
1739 default: // mehr als 3 Teile...
1740 raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
1741 "is supported. Got: %s\n",srcname));
MG Mud User88f12472016-06-24 23:31:02 +02001742 }
1743 }
1744 }
1745 // Default: nicht ignorieren.
1746 return 0;
1747}
1748
1749#ifdef __LPC_UNIONS__
1750public int TestIgnoreExt(string|string* srcnames)
1751#else
1752public int TestIgnoreExt(mixed srcnames)
1753#endif
1754{
1755 return TestIgnore(srcnames);
1756}
1757
1758// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
1759// Typen.
1760// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
1761private int check_ignores(string msg, int msg_type, string msg_action,
1762 string msg_prefix, object origin)
1763{
1764 // Einige Dinge lassen sich nicht ignorieren.
1765 if (msg_type & (MT_NEWS|MT_NOTIFICATION))
1766 return 0;
1767 // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
1768 // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
1769 // eine ignorierbare msg_action geben.
1770 else if (stringp(msg_action) && origin && origin != ME)
1771 {
1772 string srcname =
1773 (query_once_interactive(origin) ? origin->query_real_name()
1774 : origin->name(WER) || "");
1775 mapping ign = Query(P_IGNORE, F_VALUE);
1776
1777 if (member(ign, srcname))
1778 return MSG_IGNORED;
1779 // vielleicht wird irgendwas a la name.aktion ignoriert?
1780 // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
1781 // Ebenenmeldungen)
1782 if (member(ign, srcname+"."+msg_action))
1783 return MSG_VERB_IGN;
1784 // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
1785 // das reichlich sinnfrei ist.
1786 if (member(ign, "."+msg_action))
1787 return MSG_VERB_IGN;
1788 // Spieler auf Ebenen ignoriert?
1789 // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
1790 if (strstr(msg_action, MA_CHANNEL) == 0)
1791 {
1792 // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
1793 if (member(ign, srcname + "."MA_CHANNEL))
1794 return MSG_IGNORED;
1795 // spieler.ebenen.ebenenname ist oben schon abgedeckt.
1796 // .ebenen halte ich fuer sinnfrei, nicht geprueft.
1797 }
1798 // Spieler aus anderem mud? *seufz*
1799 if (strstr(srcname,"@") > -1)
1800 {
1801 string *srcparts = explode(srcname,"@");
1802 if (sizeof(srcparts)==2)
1803 {
1804 // spieler@?
1805 if (member(ign, srcparts[0]+"@"))
1806 return MSG_IGNORED;
1807 // oder Mud per @mud?
1808 if (member(ign, "@" + srcparts[1]))
1809 return MSG_MUD_IGN;
1810 // BTW: spieler@mud wurde schon ganz oben erfasst.
1811 }
1812 }
1813 }
1814 // Default: nicht ignorieren.
1815 return 0;
1816}
1817
1818// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
1819// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
Bugfixe0fc68f2022-01-07 15:30:54 +01001820// BLindheit/Taubheit). In Zukunft koennten aber weitere Typen und weitere
1821// Kriterien wichtig werden und weitere Argumente uebergeben werden. (Dies
1822// bitte beachten, falls diese Funktion protected/public werden sollte.)
MG Mud User88f12472016-06-24 23:31:02 +02001823// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
1824// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
1825// Sinn gar nix zu tun hat.
1826// Rueckgabe: 0 oder MSG_SENSE_BLOCK
Bugfixe0fc68f2022-01-07 15:30:54 +01001827private int check_senses(int msg_type)
MG Mud User88f12472016-06-24 23:31:02 +02001828{
1829 int senses = msg_type & (MT_LOOK|MT_LISTEN);
1830 // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
1831 // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
1832 // unterdrueckt werden.
1833 if (!senses)
1834 return 0;
1835
1836 if ((senses & MT_LOOK) && CannotSee(1))
1837 senses &= ~MT_LOOK; // Sinn loeschen
1838
1839 if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
1840 senses &= ~MT_LISTEN;
1841
1842 // wenn kein Sinn mehr ueber, wird die Nachricht nicht wahrgenommen.
1843 if (!senses)
1844 return MSG_SENSE_BLOCK;
1845
1846 return 0;
1847}
1848
1849public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
1850 string msg_prefix, object origin)
1851{
1852 if (!msg) return MSG_FAILED;
1853
1854 // Flags und Typen spalten
1855 int flags = msg_type & MSG_ALL_FLAGS;
1856 int type = msg_type & ~flags;
1857
1858 // ggf. defaults ermitteln
1859 origin ||= previous_object();
1860 msg_action ||= comm_guess_action();
1861 type ||= comm_guess_message_type(msg_action, origin);
1862
1863 // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
1864 if (msg_type & MT_DEBUG)
1865 {
1866 if (!QueryProp(P_WIZ_DEBUG)
1867 || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
1868 return MSG_FAILED;
1869 }
1870
1871 // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
1872 // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
1873 // Weiterbearbeitung.
1874 // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
1875 if (!(flags & MSG_DONT_IGNORE))
1876 {
1877 // Sinne pruefen. (nur typen uebergeben, keine Flags)
Bugfixe0fc68f2022-01-07 15:30:54 +01001878 int res=check_senses(type);
MG Mud User88f12472016-06-24 23:31:02 +02001879 if (res) return res;
1880
1881 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
1882 res=check_ignores(msg, type, msg_action, msg_prefix, origin);
1883 if (res) return res;
1884 }
1885
1886 // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
1887 if ((type & MT_COMM))
1888 {
1889 // erstmal in der Komm-History ablegen, wenn gewuenscht.
1890 if ((!(flags & MSG_DONT_STORE)))
1891 {
1892 string uid;
1893 if (query_once_interactive(origin))
1894 uid = origin->query_real_name();
1895 else
1896 uid = origin->name(WER) || "<unbekannt>";
Zesstraa31cd5c2016-12-17 20:11:07 +01001897 add_to_tell_history(uid, 0, 1, msg, msg_prefix,
1898 (msg_action == MA_TELL ? MSGFLAG_TELL : 0 ) );
MG Mud User88f12472016-06-24 23:31:02 +02001899 }
1900
1901 // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
1902 // Ebenenmeldungen. (Die haben ggf. schon.)
1903 if (stringp(msg_action) && QueryProp(P_AWAY)
1904 && strstr(msg_action, MA_CHANNEL) != 0)
1905 {
1906 // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
1907 if (msg[<1] == '\n')
1908 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1909 else
1910 msg = msg + " [" + strftime("%H:%M",time()) + "]";
1911 }
1912 // Kobold erlaubt und gewuenscht? Kobold ist fuer die
1913 // direkte Kommunikation mittels MT_COMM vorgesehen.
1914 // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
1915 // ignorieren.
1916 // die if-Konstruktion ist so, weil ich das _flush_cache() im else
1917 // brauche.
1918 if (query_editing(this_object()) || query_input_pending(this_object())
1919 || QueryProp(P_EARMUFFS))
1920 {
1921 if (!(flags & MSG_DONT_BUFFER)
1922 && QueryProp(P_BUFFER))
1923 {
1924 // Nachricht soll im Kobold gespeichert werden.
1925 return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
1926 }
1927 }
1928 else
1929 {
1930 // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
1931 // entleeren.
1932 _flush_cache(0);
1933 }
1934
1935 // ggf. Piepston anhaengen. NACH Koboldablage, die sollen erstmal keinen
1936 // Pieps kriegen.
1937 if (comm_beep())
1938 msg=msg + MESSAGE_BEEP;
1939 }
1940
1941 // Ausgabenachricht bauen und an den Spieler senden.
1942 if (flags & MSG_DONT_WRAP)
1943 msg = (msg_prefix ? msg_prefix : "") + msg;
1944 else
1945 {
1946 int bsflags = flags & MSG_ALL_BS_FLAGS;
1947 if (QueryProp(P_MESSAGE_PREPEND))
1948 bsflags |= BS_PREPEND_INDENT;
1949 msg = break_string(msg, 78, msg_prefix, bsflags);
1950 }
1951 efun::tell_object(ME, msg);
1952
1953 return MSG_DELIVERED;
1954}