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