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