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