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