blob: dc447b78928ec93289e06429165dc5ecb682ba26 [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
535private int comm_beep() {
Bugfix3afcb792022-01-21 22:32:42 +0100536 if (QueryProp(P_ALERT) & AL_NO_SOUND) return 0; // kein ton
Zesstra04f613c2019-11-27 23:32:54 +0100537 int beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
MG Mud User88f12472016-06-24 23:31:02 +0200538 if (!beep_interval || ((time()-last_beep_time) < beep_interval)) return 0;
539 last_beep_time=time();
540 return 1;
541}
542
543private varargs void add_to_tell_history( string uid, int sent, int recv,
544 string message, string indent, int flags )
545{
546 /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
547 Als Wert ist eine Strukur vom Typ chat_s eingetragen.
548 Strukturen chat_s und stored_msg_s sind in /std/player/comm_structs.c
549 definiert.
550 TODO fuer spaeter, gerade keine Zeit fuer:
551 Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
552 jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
553 weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
554 Element ist aeltestes Gespraech).
555 */
556
557 //TODO: Entfernen, wenn das nicht mehr passiert.
558 if (!stringp(uid))
559 {
560 ReceiveMsg(sprintf(
561 "\nadd_to_tell_history(): got bad uid argument %O."
562 "sent: %d, recv: %d, flags: %d, msg: %s",
563 uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);
564 }
Zesstraa31cd5c2016-12-17 20:11:07 +0100565
MG Mud User88f12472016-06-24 23:31:02 +0200566 // letzten Gespraechspartner fuer erwidere.
Zesstraa31cd5c2016-12-17 20:11:07 +0100567 if (flags & (MSGFLAG_TELL|MSGFLAG_RTELL))
MG Mud User88f12472016-06-24 23:31:02 +0200568 last_comm_partner = uid;
569
570 // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
571 // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
572 // Anfang.
573 if (sent) {
574 if (!sizeof(commreceivers))
575 commreceivers = ({uid});
576 else if (commreceivers[0] != uid) {
577 // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
578 // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
579 // entfernen.
580 // TODO: Effizienter gestalten.
581 commreceivers = ({uid}) + (commreceivers-({uid}));
582 // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
583 // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
584 // commreceivers drin sind.)
585 if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
586 commreceivers = commreceivers[0..MAX_SAVED_CHATS];
587 }
588 }
589
590 if (!tell_history_enabled)
591 return;
592
593 if (!indent && message[<1] == 10)
594 message = message[..<2];
595
596 struct chat_s chat;
597 // Gespraechspartner unbekannt?
598 if (!member(tell_history, uid)) {
599 // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
600 // dazu kommt.
601 if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
602 string deluid;
603 int zeit = __INT_MAX__;
604 foreach(string tuid, chat : tell_history) {
605 // aeltestes Gespraech suchen
606 if (zeit > chat->time_last_msg) {
607 deluid = tuid;
608 zeit = chat->time_last_msg;
609 }
610 }
611 // aeltestes Gespraech raus.
612 m_delete(tell_history, deluid);
613 if (member(commreceivers,deluid)>-1)
614 commreceivers-=({deluid});
615 }
616 // neues Gespraech anlegen
617 chat = (<chat_s> uid: uid, time_first_msg: time(),
618 time_last_msg: time(),
619 sentcount: sent, recvcount: recv,
620 msgbuf: 0, ptr: 0 );
621 tell_history[uid] = chat;
622 }
623 else {
624 // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
625 chat = tell_history[uid];
626 chat->time_last_msg = time();
627 chat->sentcount += sent;
628 chat->recvcount += recv;
629 }
630
631 if (tell_history_enabled < TELLHIST_ENABLED)
632 return;
633
634 // ggf. Array fuer Messages anlegen
635 if (!pointerp(chat->msgbuf))
636 chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
637
638 // Message-Struktur ermitteln oder neu anlegen
639 struct stored_msg_s msg;
640 if (!structp(chat->msgbuf[chat->ptr])) {
641 // neue Struct ins Array schreiben
642 chat->msgbuf[chat->ptr] = msg = (<stored_msg_s>);
643 }
644 else {
645 // alte Struct ueberschreiben
646 msg = chat->msgbuf[chat->ptr];
647 }
648 // Index auf naechste Messagestruktur ermitteln
649 chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
650 // Message speichern
651 msg->msg = message;
652 msg->prefix = indent;
653 msg->timestamp = time();
654}
655
656protected void clear_tell_history()
657{
658 /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
659 sofern der Spieler nichts abweichendes eingestellt hat. */
660
661#ifdef TELLHIST_LONGLIFE
662 if (tell_history_enabled == TELLHIST_LONGLIFE)
663 return;
664#endif
665
666 foreach (string uid, struct chat_s chat: tell_history)
667 if (pointerp(chat->msgbuf)) {
668 chat->msgbuf = 0;
669 chat->ptr = 0;
670 }
671}
672
673protected void reset(void)
674{
675 /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
676 Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
677 wie bei einem "schlafe ein" die Mitteilungen geloescht. */
678
679 if (!interactive())
680 clear_tell_history();
681
682}
683
684// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
685// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
686// werden soll.
687private void _send(object ob, string msg, int msg_type,
688 string msg_action, string msg_prefix)
689{
690 int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
691 switch(res) {
692 case MSG_DELIVERED:
693 break; // nix machen
694 case MSG_BUFFERED:
Zesstra62b4a862022-12-23 20:08:16 +0100695 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200696 "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
697 "genommen. Er wird sie spaeter weiterleiten!",
698 MT_NOTIFICATION, msg_action, 0, this_object());
699 break;
700 case MSG_IGNORED:
701 case MSG_VERB_IGN:
702 case MSG_MUD_IGN:
703 ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
704 MT_NOTIFICATION, msg_action, 0, this_object());
705 break;
706 case MSG_SENSE_BLOCK:
707 ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
708 MT_NOTIFICATION, msg_action, 0, this_object());
709 break;
710 case MSG_BUFFER_FULL:
Zesstra62b4a862022-12-23 20:08:16 +0100711 ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden. "
MG Mud User88f12472016-06-24 23:31:02 +0200712 "Die Mitteilung ging verloren, denn der Kobold kann sich "
713 "nichts mehr merken!", MT_NOTIFICATION, msg_action,
714 0, this_object());
715 break;
716 default:
717 ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
718 "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
719 break;
720 }
721}
722
723// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
724// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
725// die Nachricht ggf. in den Kommhistory erfasst wird.
726// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
727protected varargs int _recv(object ob, string message, int flag, string indent)
728{
729 write(break_string(message, 78, indent,
730 QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
731 if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
732 query_once_interactive(ob))
733 {
734 if (flag & MSGFLAG_WHISPER)
735 add_to_tell_history(getuid(ob), 1, 0,
736 "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
737 flag);
738 else
739 add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
740 }
741 return 1;
742}
743
744// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
745// Fehlerbehandlung) ist es jedoch auch mal ein String.
746varargs int Message(string msg, int flag, string indent,
747 string cname, mixed sender)
748{
749 object ti;
750 string verb, reply, *ignore, tin;
751 int em, te;
752 mixed deaf;
753
754 // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
755 // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
756 // damit 'ignoriere name.debug' funktioniert.
757 if( flag == MSGFLAG_CHANNEL ){
758 if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
759 || msg[1..9]=="Warnungen"))
760 {
761 // Missbrauch der Variable 'ignore' als Zwischenspeicher
762 ignore = regexplode( msg, ":| |\\]" );
763 verb = lower_case(ignore[0][1..]);
764 tin = lower_case(ignore[2]);
765 }
766 else
767 {
768 if(cname)
769 verb=lower_case(cname);
770 else
771 verb=query_verb();
772 if( ti = this_interactive() )
773 {
774 tin = getuid(this_interactive());
775 }
776 else
777 {
778 //falls doch kein Objekt...
779 if (objectp(sender))
780 tin=lower_case(sender->name(RAW)||"<Unbekannt>");
781 }
782 }
783 }
784 else {
785 if( ti = this_interactive() )
786 tin = getuid(this_interactive());
787 verb = query_verb();
788 }
789
790 te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
791
792 // fuer "erwidere"
793 if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
794 if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
795 if (flag & MSGFLAG_WHISPER)
796 add_to_tell_history(getuid(ti), 0, 1,
797 capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
798 (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
799 ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
800 " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
801 else
802 add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
803 }
804 }
805 // Hoert der Spieler nicht?
806 em = (ti &&
807 (te || flag & MSGFLAG_SHOUT) &&
808 (QueryProp(P_EARMUFFS) &&
809 (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
810 ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
811
812 // Werden der Sender oder das Verb ignoriert?
813 if(!ti && tin && flag == MSGFLAG_CHANNEL)
814 {
815 if((member(ignore, tin) != -1))
816 {
817 return MESSAGE_IGNORE_YOU;
818 }
819 if(verb && sizeof(filter(ignore, #'check_ignore, verb, tin)) )
820 {
821 return MESSAGE_IGNORE_YOU;
822 }
823 }
824 if (ti && (member(ignore, getuid(ti)) != -1)) {
825 if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
826 efun::tell_object(ti, capitalize(name())+
827 " hoert gar nicht zu, was Du sagst.\n");
828 return MESSAGE_IGNORE_YOU;
829 }
830 if(tin && verb &&
831 sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
832 {
833 if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
834 verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
835 efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
836 return MESSAGE_IGNORE_VERB;
837 }
838 if (flag & MSGFLAG_RTELL) {
839 int at;
840
841 verb = lower_case(old_explode(msg, " ")[0][1..]);
842 at = member(verb, '@');
843 /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
844 add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
845
846 if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
847 return MESSAGE_IGNORE_YOU;
848 else if (at > 0 && member(ignore, verb[at..]) >= 0)
849 return MESSAGE_IGNORE_MUD;
850 }
851
852 // Taubheit/Oropax
853 te |= (flag & MSGFLAG_SAY);
854
855 if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
856 deaf = QueryProp(P_DEAF);
857 if (te)
858 reply = stringp(deaf) ?
859 capitalize(sprintf(deaf, name())) :
860 capitalize(name())+" ist momentan leider taub.\n";
861 }
862 else if (em)
863 reply = capitalize(name())+" hat Oropax in den Ohren.\n";
864
865 msg = break_string(msg, 78, indent,
866 (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
867
868 if(QueryProp(P_BUFFER) &&
869 (deaf ||
870 query_editing(this_object()) ||
871 query_input_pending(this_object())))
872 {
873 deaf = MESSAGE_DEAF;
874 if(flag & MSGFLAG_CACHE)
875 {
876 if(!stringp(reply))
877 reply = name()+" moechte gerade nicht gestoert werden.\n";
878
879 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
880
881 int res = add_to_kobold(msg, 0, 0, 0,
882 objectp(sender) ? sender : ME);
883 if(res == MSG_BUFFERED)
884 {
885
886 reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
887 "genommen.\nEr wird sie spaeter weiterleiten!";
888 deaf = MESSAGE_CACHE;
889 }
890 else {
891 reply += "Die Mitteilung ging verloren, denn "+
892 "der Kobold kann sich nichts mehr merken!";
893 deaf = MESSAGE_CACHE_FULL;
894 }
895 if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
896 efun::tell_object(ti, reply+"\n");
897 }
898 return deaf;
899 }
900 else if((deaf || em) &&
901 ( (flag & MSGFLAG_RTELL) ||
902 (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
903 if (te && ti)
904 efun::tell_object(ti, reply);
905 return MESSAGE_DEAF;
906 }
907
908 _flush_cache(0);
909 if(te && QueryProp(P_AWAY))
910 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
911
912 if (flag & (MSGFLAG_SAY | MSGFLAG_TELL) && comm_beep()) {
913 msg=MESSAGE_BEEP+msg;
914 }
915 efun::tell_object(ME, msg);
916 return MESSAGE_OK;
917}
918
919static int ignoriere(string str)
920{
921 str = _unparsed_args(1);
922 mapping ignore=Query(P_IGNORE, F_VALUE);
923
924 if (!str)
925 {
926 string* ignarr = m_indices(ignore);
927 if (!sizeof(ignarr))
928 tell_object(ME, "Du ignorierst niemanden.\n");
929 else
930 ReceiveMsg("Du ignorierst:\n"
931 + break_string(CountUp(map(sort_array(ignarr, #'> ),
932 #'capitalize )
933 ) + ".",78),
934 MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
935 0,0,this_object());
936 return 1;
937 }
938 // trim spaces from args and convert to lower case.
939 str = lower_case(trim(str, TRIM_BOTH));
940
941 if (member(ignore, str))
942 {
943 RemoveIgnore(str);
944 tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
945 }
946 else if (sizeof(ignore)>100)
947 {
948 tell_object(ME, "Du ignorierst schon genuegend!\n");
949 }
950 else if (AddIgnore(str) == 1)
951 {
952 tell_object(ME,
953 sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
954 }
955 else
956 {
957 tell_object(ME,
958 sprintf("'%s' kannst Du nicht ignorieren.\n",str));
959 }
960 return 1;
961}
962
963
964static int _msg_beep(string str) {
965 int beep_interval;
966 notify_fail("Syntax: klingelton <1 bis 3600 Sekunden> oder klingelton aus\n");
967 if (stringp(str)) {
968 if (str=="aus")
969 SetProp(P_MESSAGE_BEEP,0);
970 else if ((beep_interval=to_int(str)) > 0 && beep_interval<=3600)
971 SetProp(P_MESSAGE_BEEP,beep_interval);
972 else return 0;
973 }
974
Zesstra593b2c72019-11-27 23:37:03 +0100975 beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
Bugfixb1d9b4d2021-09-14 20:07:04 +0200976 ReceiveNotify("Ton bei Mitteilungen: "+
MG Mud User88f12472016-06-24 23:31:02 +0200977 (beep_interval ? "aller "+beep_interval+" Sekunden." : "aus."),
978 query_verb());
979 return 1;
980}
981
982static int _msg_prepend(string str) {
MG Mud User88f12472016-06-24 23:31:02 +0200983 notify_fail("Syntax: senderwiederholung ein/aus\n");
984 if (stringp(str)) {
985 if (str=="aus")
986 SetProp(P_MESSAGE_PREPEND,1);
987 else if (str=="ein")
988 SetProp(P_MESSAGE_PREPEND,0);
989 else return 0;
990 }
991
Bugfixb1d9b4d2021-09-14 20:07:04 +0200992 ReceiveNotify("Senderwiederholung bei Mitteilungen: "+
Zesstra04f613c2019-11-27 23:32:54 +0100993 (({int})QueryProp(P_MESSAGE_PREPEND) ? "aus" : "ein")+".",
MG Mud User88f12472016-06-24 23:31:02 +0200994 query_verb());
995
996 return 1;
997}
998
999static int _communicate(mixed str, int silent)
1000{
1001 string verb;
1002 string myname;
1003 string msg;
1004
1005 if (!str || extern_call()) str=_unparsed_args()||"";
1006 /* str=_unparsed_args()||""; */
1007 verb = query_verb();
1008 if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
1009 if (str==""||str==" "||!str)
1010 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001011 ReceiveNotify("Was willst Du sagen?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001012 return 1;
1013 }
1014 msg=permutate(str);
1015
1016 myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
1017 !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
1018 "":QueryProp(P_PRESAY))+name())||"";
1019
1020 // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
1021 // ist, keine MT_COMM)
1022 send_room(environment(), msg, MT_LISTEN, MA_SAY,
1023 capitalize(myname)+" sagt: ", ({this_object()}) );
1024
1025 if(!silent)
1026 {
1027 ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1028 MA_SAY, "Du sagst: ", ME);
1029 }
1030 return 1;
1031}
1032
1033static int _shout_to_all(mixed str)
1034{
1035 string pre, myname, realname, wizards_msg, players_msg;
1036 string wizard_prefix, player_prefix;
1037 int chars;
1038
1039 if (!(str=_unparsed_args()))
1040 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001041 ReceiveNotify("Was willst Du rufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001042 return 1;
1043 }
1044 chars=sizeof(str)/2;
1045 if (chars<4) chars=4;
1046 pre = (!IS_LEARNER(ME) ||
1047 QueryProp(P_INVIS) ||
1048 !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
1049 realname = capitalize((pre + capitalize(getuid()))||"");
1050 myname = capitalize(pre + name()||"");
1051 if (QueryProp(P_INVIS))
1052 realname = "("+realname+")";
1053
1054 wizards_msg = permutate(str);
1055 wizard_prefix = myname+" ruft: ";
1056
1057 if(QueryProp(P_FROG)) {
1058 players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
1059 player_prefix = myname+" quakt: ";
1060 }
1061 else {
1062 players_msg = wizards_msg;
1063 player_prefix = wizard_prefix;
1064 }
1065
1066 if(!IS_LEARNER(this_player()))
1067 {
1068 if(QueryProp(P_GHOST)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001069 ReceiveNotify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
MG Mud User88f12472016-06-24 23:31:02 +02001070 MA_SHOUT);
1071 return 1;
1072 }
1073 if (QueryProp(P_SP) <(chars+20))
1074 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001075 ReceiveNotify("Du musst erst wieder magische Kraefte sammeln.",
MG Mud User88f12472016-06-24 23:31:02 +02001076 MA_SHOUT);
Bugfixb1d9b4d2021-09-14 20:07:04 +02001077 ReceiveNotify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
MG Mud User88f12472016-06-24 23:31:02 +02001078 "Ebenen').", MA_SHOUT);
1079 return 1;
1080 }
1081 SetProp(P_SP, QueryProp(P_SP) - chars - 20);
1082 }
1083
1084 ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
1085 "rufe", "Du rufst: ", ME);
1086
1087 foreach ( object ob : users()-({this_object()}) )
1088 if ( IS_LEARNER(ob) )
1089 ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR, MA_SHOUT, wizard_prefix,
1090 this_object());
1091 else
1092 ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR, MA_SHOUT, player_prefix,
1093 this_object());
1094
1095 return 1;
1096}
1097
1098varargs int _tell(string who, mixed msg)
1099{
1100 object ob;
1101 string away,myname,ret;
Arathornb3051452021-05-13 21:13:03 +02001102 mixed it;
MG Mud User88f12472016-06-24 23:31:02 +02001103 string *xname;
1104 int i,visflag;
1105
1106 if (extern_call() && this_interactive()!=ME) return 1;
1107 if (!who || !msg) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001108 ReceiveNotify("Was willst Du mitteilen?",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001109 return 1;
1110 }
1111
1112 if(who == ERWIDER_PARAM)
1113 {
1114 if (!last_comm_partner)
1115 {
1116 _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
1117 "Du was erwidern\nkoenntest.\n");
1118 return 0;
1119 }
1120 who=last_comm_partner;
1121 }
1122
1123 // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
1124 if (who == ".")
1125 who = ".1";
1126
1127 if ( sscanf(who, ".%d", i) == 1 ) {
1128 if(i > 0 && i <= sizeof(commreceivers))
1129 who = commreceivers[i-1];
1130 else {
1131 _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
1132 return 0;
1133 }
1134 }
1135
1136 xname = explode(who, "@");
1137
1138 if (sizeof(xname) == 2)
1139 {
1140 if ( QueryProp(P_QP) )
1141 {
Zesstra04f613c2019-11-27 23:32:54 +01001142 if (ret=({string})INETD->_send_udp(xname[1],
MG Mud User88f12472016-06-24 23:31:02 +02001143 ([ REQUEST: "tell",
1144 RECIPIENT: xname[0],
1145 SENDER: getuid(ME),
1146 DATA: msg ]), 1))
1147 {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001148 ReceiveNotify(ret, MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001149 }
1150 else
1151 {
1152 write("Nachricht abgeschickt.\n");
1153 add_to_tell_history(who, 1, 0, msg,
Zesstraa31cd5c2016-12-17 20:11:07 +01001154 "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_RTELL, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001155 }
1156 }
1157 else
1158 write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
1159 "Muds etwas mitteilen zu koennen.\n");
1160 return 1;
1161 }
1162
1163 if (!ob=find_player(it = lower_case(who)))
1164 {
1165 it = match_living(it, 0);
1166 if (!stringp(it))
1167 switch(it) {
1168 case -1:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001169 ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001170 return 1;
1171 case -2:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001172 ReceiveNotify("Kein solcher Spieler!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001173 return 1;
1174 }
1175 ob = find_player(it) || find_living(it);
1176 if(!ob) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001177 ReceiveNotify("Kein solcher Spieler!",MA_TELL);
MG Mud User88f12472016-06-24 23:31:02 +02001178 return 1;
1179 }
1180 }
1181
1182 if(QueryProp(P_INVIS)){
1183 if(!IS_LEARNER(ob))
1184 myname = name();
1185 else
1186 myname="("+
1187 ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
1188 capitalize(getuid()) + ")";
1189 }
1190 else
1191 myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
Bugfix45f88ce2017-03-06 14:41:56 +01001192 QueryProp(P_PRESAY):"") + capitalize(getuid(ME));
MG Mud User88f12472016-06-24 23:31:02 +02001193 if (myname && sizeof(myname)) myname=capitalize(myname);
1194 // erstmal an Empfaenger senden
1195 _send(ob, permutate(msg), MT_COMM|MT_FAR, MA_TELL,
1196 myname + " teilt Dir mit: ");
1197
1198 // dann evtl. noch an Absender ausgeben...
1199 if (visflag = !ob->QueryProp(P_INVIS) || IS_LEARNER(this_player()))
1200 _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(it) + " mit: ");
1201 // oder irgendwas anderes an den Absender ausgeben...
1202 if (!visflag && interactive(ob))
Bugfixb1d9b4d2021-09-14 20:07:04 +02001203 ReceiveNotify("Kein solcher Spieler!",MA_TELL);
Zesstra04f613c2019-11-27 23:32:54 +01001204 else if (away = ({string})ob->QueryProp(P_AWAY))
MG Mud User88f12472016-06-24 23:31:02 +02001205 ReceiveMsg( break_string( away, 78, capitalize(it)
1206 + " ist gerade nicht da: ", BS_INDENT_ONCE ),
1207 MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
1208 MA_TELL, 0, this_object());
1209 else if (interactive(ob) && (i=query_idle(ob))>=600)
1210 { //ab 10 Mins
1211 if (i<3600)
1212 away=time2string("%m %M",i);
1213 else
1214 away=time2string("%h %H und %m %M",i);
1215
Bugfixb1d9b4d2021-09-14 20:07:04 +02001216 ReceiveNotify(sprintf("%s ist seit %s voellig untaetig.",
MG Mud User88f12472016-06-24 23:31:02 +02001217 capitalize(it),away),
1218 MA_TELL);
1219 }
1220
1221 return 1;
1222}
1223
1224static int _teile(string str)
1225{
1226 string who, message;
1227 if (!(str=_unparsed_args())) return 0;
1228 if (sscanf(str, "%s mit %s", who, message) == 2)
1229 return _tell(who, message,1);
1230 return 0;
1231}
1232static int _teile_mit_alias(string str)
1233{
1234 str = _unparsed_args(), TRIM_LEFT;
1235 if (!str) return 0;
1236 str = trim(str, TRIM_LEFT);
1237 // Ziel muss min. 2 Buchstaben haben (.<nr>)
1238 if (sizeof(str) < 4) return 0;
1239 int pos = strstr(str, " ");
1240 if (pos >= 2)
1241 return _tell(str[..pos-1], str[pos+1..]);
1242 return 0;
1243}
1244
1245static int _erzaehle(string str)
1246{
1247 string who, message;
1248
1249 if (!(str=_unparsed_args())) return 0;
1250 if (sscanf(str, "%s %s", who, message) == 2)
1251 return _tell(who, message,1);
1252 return 0;
1253}
1254
1255static int _whisper(string str)
1256{
1257 object ob;
1258 string who;
1259 string msg;
1260 string myname;
1261
1262 if (!(str=_unparsed_args()) ||
1263 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1264 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001265 ReceiveNotify("Was willst Du wem zufluestern?",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001266 return 1;
1267 }
Zesstra6e88b6a2019-11-08 00:25:39 +01001268 who = lower_case(who);
MG Mud User88f12472016-06-24 23:31:02 +02001269 if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001270 ReceiveNotify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001271 return 1;
1272 }
1273
1274 myname = capitalize((((IS_LEARNER(ME) &&
1275 !QueryProp(P_INVIS) &&
1276 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1277 QueryProp(P_PRESAY) : "") + name()) || "");
1278
1279 _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
Bugfixdbdbed52022-01-21 11:14:35 +01001280 MA_SAY, myname + " fluestert Dir zu: ");
MG Mud User88f12472016-06-24 23:31:02 +02001281 send_room(environment(),
1282 myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
1283 MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
1284
1285 _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
1286
1287
1288 return 1;
1289}
1290
1291static int _remote_whisper(string str)
1292{
1293 /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
1294 Nachricht nicht in der tell_history verewigt. */
1295
1296 object ob;
1297 string who, it;
1298 string msg;
1299 string myname;
1300
1301 if (!(str=_unparsed_args()) ||
1302 (sscanf(str, "%s zu %s", who, msg) != 2 &&
1303 sscanf(str, "%s %s", who, msg) !=2 )) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001304 ReceiveNotify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001305 return 1;
1306 }
1307
1308 if (!ob=find_player(it = lower_case(who)))
1309 {
1310 it = match_living(it, 0);
1311 if (!stringp(it))
1312 switch(it){
1313 case -1:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001314 ReceiveNotify("Das war nicht eindeutig!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001315 return 1;
1316 case -2:
Bugfixb1d9b4d2021-09-14 20:07:04 +02001317 ReceiveNotify("Kein solcher Spieler!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001318 return 1;
1319 }
1320 ob = find_player(it);
1321 if(!ob) ob = find_living(it);
1322 if(!ob){
Bugfixb1d9b4d2021-09-14 20:07:04 +02001323 ReceiveNotify("Kein solcher Spieler!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001324 return 1;
1325 }
1326 }
1327 if (environment(ob) == environment()) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001328 ReceiveNotify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001329 return 1;
1330 }
1331
1332 myname = capitalize((((IS_LEARNER(ME) &&
1333 !QueryProp(P_INVIS) &&
1334 (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
1335 QueryProp(P_PRESAY) : "") + name()) || "");
1336
1337 // An Empfaenger senden.
1338 _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE, MA_EMOTE,
1339 myname + " fluestert Dir aus der Ferne zu: ");
1340
1341 // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
1342 if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001343 ReceiveNotify("Kein solcher Spieler!",MA_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +02001344 return 1;
1345 }
1346 // sonst eigene Meldung via _recv() ausgeben.
1347 else
1348 _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
1349 "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
1350
1351 return 1;
1352}
1353
1354static int _converse(string arg)
1355{
Bugfixb1d9b4d2021-09-14 20:07:04 +02001356 ReceiveNotify("Mit '**' wird das Gespraech beendet.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001357 if (stringp(arg) && strstr(arg, "-s") == 0)
1358 input_to("_converse_more", INPUT_PROMPT, "]", 1);
1359 else
1360 input_to("_converse_more", INPUT_PROMPT, "]", 0);
1361 return 1;
1362}
1363
1364static int _converse_more(mixed str, int silent)
1365{
1366 if (str == "**") {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001367 ReceiveNotify("Ok.",MA_SAY);
MG Mud User88f12472016-06-24 23:31:02 +02001368 return 0;
1369 }
1370
1371 if(str != "")
1372 _communicate(str, silent);
1373
1374 input_to("_converse_more", INPUT_PROMPT, "]", silent);
1375 return 1;
1376}
1377
1378private int is_learner(object o) { return IS_LEARNER(o); }
1379
1380static int _shout_to_wizards(mixed str)
1381{
MG Mud User88f12472016-06-24 23:31:02 +02001382 string myname;
MG Mud User88f12472016-06-24 23:31:02 +02001383
1384 str = _unparsed_args();
1385 if (!str||!sizeof(str)) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001386 ReceiveNotify("Was willst Du den Magiern zurufen?",MA_SHOUT);
MG Mud User88f12472016-06-24 23:31:02 +02001387 return 1;
1388 }
1389 // Kontrollzeichen rausfiltern.
1390 str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
1391 myname = capitalize(getuid(this_object()));
1392 if (!IS_LEARNER(this_object()))
1393 _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
1394
1395 // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
1396 filter(users(), #'is_learner)->ReceiveMsg(str,
1397 MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE,
1398 MA_SHOUT, myname+" an alle Magier: ", this_object());
1399
1400 return 1;
1401}
1402
1403static int _echo(string str) {
1404 if (!IS_SEER(ME) || (!IS_LEARNER(ME)
1405 && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
1406 return 0;
1407
1408 if (!(str=_unparsed_args())) {
Bugfixb1d9b4d2021-09-14 20:07:04 +02001409 ReceiveNotify("Was moechtest Du 'echoen'?", 0);
MG Mud User88f12472016-06-24 23:31:02 +02001410 return 1;
1411 }
1412
1413 if (!IS_LEARNER(this_interactive()))
1414 {
1415 if (QueryProp(P_GHOST))
1416 {
1417 _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
1418 return 0;
1419 }
1420 if (QueryProp(P_SP)<ECHO_COST)
1421 {
1422 _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
1423 return 0;
1424 }
1425 SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
1426 str=">\b"+str;
1427 log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
1428 str));
1429 }
1430 // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
1431 // Kommhistory noch im Kobold landen.
1432 send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
1433 MA_UNKNOWN, 0, 0);
1434 return 1;
1435}
1436
1437// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
1438// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
1439// und gefedert.
1440static string *_set_ignore(mixed arg)
1441{
1442 raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
1443 "Benutze AddIgnore/RemoveIgnore()!\n");
1444}
1445// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
1446// nix dran, dass alle TestIgnore() & Co benutzen sollen.
1447static string *_query_ignore() {
1448 mixed ign=Query(P_IGNORE, F_VALUE);
1449 if (mappingp(ign))
1450 return m_indices(ign);
1451 return ({});
1452}
1453
1454public int AddIgnore(string ign) {
1455 // Einige strings sind nicht erlaubt, z.B. konsekutive .
1456 if (!sizeof(ign)
1457 || regmatch(ign,"[.]{2,}",RE_PCRE)
1458 || regmatch(ign," ",RE_PCRE)
1459 || sizeof(explode(ign,"."))>3)
1460 return 0;
1461
1462 mapping ignores=Query(P_IGNORE, F_VALUE);
1463 ignores[ign]=time();
1464 // kein Set() noetig.
1465 return 1;
1466}
1467
1468public int RemoveIgnore(string ign)
1469{
1470 mapping ignores=Query(P_IGNORE,F_VALUE);
1471 m_delete(ignores,ign);
1472 // Kein Set() noetig
1473 return 1;
1474}
1475
1476static int _query_intermud()
1477{
1478 mixed tmp;
1479 return member(pointerp(tmp=Query(P_CHANNELS))?tmp:({}), "Intermud") > -1;
1480}
1481
1482
1483int erwidere(string str)
1484{
1485 str=_unparsed_args();
1486 if (!str) return 0;
1487 return _tell(ERWIDER_PARAM, str ,1);
1488}
1489
1490static int tmhist(string str)
1491{
1492
1493 if (str == "aus") {
1494 tell_history_enabled = TELLHIST_DISABLED;
1495 write("Ok, es wird nichts mehr gespeichert.\n");
1496 if (sizeof(tell_history)) {
1497 tell_history = ([]);
1498 commreceivers = ({});
1499 write("Deine Mitteilungsgeschichte wurde geloescht.\n");
1500 }
1501 return 1;
1502 }
1503
1504 if (str == "namen") {
1505 int flag;
1506 tell_history_enabled = TELLHIST_NO_MESSAGE;
1507 write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
1508 foreach (string uid, struct chat_s chat: tell_history)
1509 if (pointerp(chat->msgbuf)) {
1510 chat->msgbuf = 0;
1511 chat->ptr = 0;
1512 flag = 1;
1513 }
1514 if (flag)
1515 write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
1516 return 1;
1517 }
1518
1519 if (str == "ein" || str == "an") {
1520 tell_history_enabled = TELLHIST_ENABLED;
1521 write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
1522 return 1;
1523 }
1524
1525#ifdef TELLHIST_LONGLIFE
1526 if (str == "langlebig") {
1527 tell_history_enabled = TELLHIST_LONGLIFE;
1528 write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
1529 "Ende/Crash/\nReboot gespeichert.\n");
1530 return 1;
1531 }
1532#endif
1533
1534 if (str == "status") {
1535 switch (tell_history_enabled) {
1536 case TELLHIST_DISABLED:
1537 write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
1538 break;
1539 case TELLHIST_NO_MESSAGE:
1540 write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
1541 break;
1542 case TELLHIST_ENABLED:
1543 write("Deine Mitteilungen werden gespeichert.\n");
1544 break;
1545#ifdef TELLHIST_LONGLIFE
1546 case TELLHIST_LONGLIFE:
1547 write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
1548 "Crash/Reboot\ngespeichert.\n");
1549 break;
1550#endif
1551 }
1552 return 1;
1553 }
1554
1555 if (tell_history_enabled == TELLHIST_DISABLED) {
1556 _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
1557 return 0;
1558 }
1559
1560 if (!sizeof(tell_history)) {
1561 _notify_fail("Du hast noch keinem etwas mitgeteilt "
1562 "und noch keine Mitteilungen erhalten.\n");
1563 return 0;
1564 }
1565
1566 if (str && sizeof(str)) {
1567
1568 if (tell_history_enabled < TELLHIST_ENABLED) {
1569 _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
1570 return 0;
1571 }
1572
1573 string uid;
1574 if (member(tell_history, str)) {
1575 // Name gewuenscht, da der String in der History vorkommt.
1576 uid = str;
1577 }
1578 else {
1579 // evtl. ne Zahl angegeben.
1580 int i;
1581 string *partners = sorted_commpartners(0);
1582 if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
1583 uid = partners[i];
1584 else {
1585 notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
1586 return 0;
1587 }
1588 }
1589
1590 mixed *data = tell_history[uid]->msgbuf;
1591 if (!data) {
1592 _notify_fail(
1593 "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
1594 return 0;
1595 }
1596
1597 int ptr = tell_history[uid]->ptr;
1598
1599 More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
1600 data[0..ptr-1],
1601 function string (struct stored_msg_s msg) {
1602 if (!structp(msg)) return "";
1603 return break_string( msg->msg + " <"
1604 + strftime("%H:%M:%S",msg->timestamp) + ">", 78,
1605 msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
1606 } ) ) );
1607 return 1;
1608 }
1609
1610 string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
1611 int i;
1612 foreach (string uid : sorted_commpartners(0) ) {
1613 int j;
1614 struct chat_s chat = tell_history[uid];
1615 history += sprintf("%2d.%-4s %s %-11s %d gesendet/%d empfangen\n", ++i,
1616 ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
1617 strftime("%a, %e.%m.%y",chat->time_last_msg),
1618 capitalize(chat->uid), chat->sentcount, chat->recvcount);
1619 }
1620
1621 More(history);
1622
1623 return 1;
1624}
1625
1626static mixed _query_localcmds()
1627{
1628 return ({
1629 ({"kobold", "cmd_kobold",0,0}),
1630 ({"sag","_communicate",0,0}),
1631 ({"sage","_communicate",0,0}),
1632 ({"'","_communicate",1,0}),
1633 ({"mruf","_shout_to_wizards",0,0}),
1634 ({"mrufe","_shout_to_wizards",0,0}),
1635 ({"ruf","_shout_to_all",0,0}),
1636 ({"rufe","_shout_to_all",0,0}),
1637 ({"erzaehl","_erzaehle",0,0}),
1638 ({"erzaehle","_erzaehle",0,0}),
1639 ({"teil","_teile",0,0}),
1640 ({"teile","_teile",0,0}),
1641 ({"tm","_teile_mit_alias",0,0}),
1642 ({"fluester","_whisper",0,0}),
1643 ({"fluestere","_whisper",0,0}),
1644 ({"rfluester","_remote_whisper",0,0}),
1645 ({"rfluestere","_remote_whisper",0,0}),
1646 ({"gespraech","_converse",0,0}),
1647 ({"echo","_echo",0,0}),
1648 ({"ignorier","ignoriere",0,0}),
1649 ({"ignoriere","ignoriere",0,0}),
1650 ({"tmhist","tmhist",0,0}),
1651 ({"erwider","erwidere",0,0}),
1652 ({"erwidere","erwidere",0,0}),
1653 ({"klingelton","_msg_beep",0,0}),
1654 ({"senderwiederholung","_msg_prepend",0,0}),
1655 ({"report","set_report",0,0}),
1656 })+channel::_query_localcmds();
1657}
1658
1659private string *sorted_commpartners(int reversed) {
1660 return sort_array(m_indices(tell_history),
1661 function int (string uid1, string uid2) {
1662 if (reversed)
1663 return tell_history[uid1]->time_last_msg >
1664 tell_history[uid2]->time_last_msg;
1665 else
1666 return tell_history[uid1]->time_last_msg <=
1667 tell_history[uid2]->time_last_msg;
1668 } );
1669}
1670
Zesstra0712bac2020-06-12 09:41:24 +02001671// Setzt den Prompt eines Interactives. Gerufen bei Objekterstellung,
1672// Reconnect und bei Magiern, wenn diese ihren Prompt oder ihr aktuelles
1673// Verzeichnis aendern.
MG Mud User88f12472016-06-24 23:31:02 +02001674static void modify_prompt() {
1675 string text = Query(P_PROMPT, F_VALUE);
1676
1677 if ( !stringp(text) || !sizeof(text) )
1678 text = "> ";
1679 else {
1680 string path = Query(P_CURRENTDIR, F_VALUE);
1681 if (stringp(path) && sizeof(path))
1682 text = regreplace(text,"\\w",path,0); // Pfad einsetzen
1683 }
1684 configure_interactive(this_object(), IC_PROMPT, text);
1685}
1686
1687// Prueft auf Ingoriereeintraege.
1688// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
MG Mud User88f12472016-06-24 23:31:02 +02001689public int TestIgnore(string|string* srcnames)
MG Mud User88f12472016-06-24 23:31:02 +02001690{
1691 mapping ign = Query(P_IGNORE, F_VALUE);
1692 if (stringp(srcnames))
1693 srcnames = ({srcnames});
1694
1695 foreach(string srcname: srcnames)
1696 {
1697 // einfachster Fall, exakter Match
1698 if (member(ign, srcname))
1699 return MSG_IGNORED;
1700 // ansonsten muss aufgetrennt werden.
1701 if (strstr(srcname,".") > -1)
1702 {
1703 string *srcparts=explode(srcname,".");
1704 switch(sizeof(srcparts))
1705 {
1706 // case 0 und 1 kann nicht passieren.
1707 case 3:
1708 // zu pruefen: [sender].aktion.qualifizierer.
1709 // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
1710 // oben schon geprueft. im Spieler geprueft werden muss noch:
1711 // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
1712 if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
1713 || member(ign, "."+srcparts[1]) // .aktion
1714 || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
1715 || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
1716 )
1717 {
1718 return MSG_IGNORED;
1719 }
1720 break;
1721 case 2:
1722 // zu pruefen: spieler.aktion
1723 // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
1724 // oben schon geprueft. Im Spieler zu pruefen ist noch:
1725 // spieler und .aktion
1726 if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
1727 || member(ign, "."+srcparts[1]))
1728 {
1729 return MSG_IGNORED;
1730 }
1731 break;
1732 default: // mehr als 3 Teile...
1733 raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
1734 "is supported. Got: %s\n",srcname));
MG Mud User88f12472016-06-24 23:31:02 +02001735 }
1736 }
1737 }
1738 // Default: nicht ignorieren.
1739 return 0;
1740}
1741
1742#ifdef __LPC_UNIONS__
1743public int TestIgnoreExt(string|string* srcnames)
1744#else
1745public int TestIgnoreExt(mixed srcnames)
1746#endif
1747{
1748 return TestIgnore(srcnames);
1749}
1750
1751// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
1752// Typen.
1753// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
1754private int check_ignores(string msg, int msg_type, string msg_action,
1755 string msg_prefix, object origin)
1756{
1757 // Einige Dinge lassen sich nicht ignorieren.
1758 if (msg_type & (MT_NEWS|MT_NOTIFICATION))
1759 return 0;
1760 // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
1761 // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
1762 // eine ignorierbare msg_action geben.
1763 else if (stringp(msg_action) && origin && origin != ME)
1764 {
1765 string srcname =
1766 (query_once_interactive(origin) ? origin->query_real_name()
1767 : origin->name(WER) || "");
1768 mapping ign = Query(P_IGNORE, F_VALUE);
1769
1770 if (member(ign, srcname))
1771 return MSG_IGNORED;
1772 // vielleicht wird irgendwas a la name.aktion ignoriert?
1773 // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
1774 // Ebenenmeldungen)
1775 if (member(ign, srcname+"."+msg_action))
1776 return MSG_VERB_IGN;
1777 // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
1778 // das reichlich sinnfrei ist.
1779 if (member(ign, "."+msg_action))
1780 return MSG_VERB_IGN;
1781 // Spieler auf Ebenen ignoriert?
1782 // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
1783 if (strstr(msg_action, MA_CHANNEL) == 0)
1784 {
1785 // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
1786 if (member(ign, srcname + "."MA_CHANNEL))
1787 return MSG_IGNORED;
1788 // spieler.ebenen.ebenenname ist oben schon abgedeckt.
1789 // .ebenen halte ich fuer sinnfrei, nicht geprueft.
1790 }
1791 // Spieler aus anderem mud? *seufz*
1792 if (strstr(srcname,"@") > -1)
1793 {
1794 string *srcparts = explode(srcname,"@");
1795 if (sizeof(srcparts)==2)
1796 {
1797 // spieler@?
1798 if (member(ign, srcparts[0]+"@"))
1799 return MSG_IGNORED;
1800 // oder Mud per @mud?
1801 if (member(ign, "@" + srcparts[1]))
1802 return MSG_MUD_IGN;
1803 // BTW: spieler@mud wurde schon ganz oben erfasst.
1804 }
1805 }
1806 }
1807 // Default: nicht ignorieren.
1808 return 0;
1809}
1810
1811// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
1812// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
Bugfixe0fc68f2022-01-07 15:30:54 +01001813// BLindheit/Taubheit). In Zukunft koennten aber weitere Typen und weitere
1814// Kriterien wichtig werden und weitere Argumente uebergeben werden. (Dies
1815// bitte beachten, falls diese Funktion protected/public werden sollte.)
MG Mud User88f12472016-06-24 23:31:02 +02001816// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
1817// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
1818// Sinn gar nix zu tun hat.
1819// Rueckgabe: 0 oder MSG_SENSE_BLOCK
Bugfixe0fc68f2022-01-07 15:30:54 +01001820private int check_senses(int msg_type)
MG Mud User88f12472016-06-24 23:31:02 +02001821{
1822 int senses = msg_type & (MT_LOOK|MT_LISTEN);
1823 // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
1824 // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
1825 // unterdrueckt werden.
1826 if (!senses)
1827 return 0;
Bugfix427a5812022-01-07 15:41:24 +01001828
1829 int orig_senses = senses;
1830
MG Mud User88f12472016-06-24 23:31:02 +02001831 if ((senses & MT_LOOK) && CannotSee(1))
1832 senses &= ~MT_LOOK; // Sinn loeschen
1833
1834 if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
1835 senses &= ~MT_LISTEN;
1836
Bugfix427a5812022-01-07 15:41:24 +01001837 // Wenn kein Sinn mehr ueber ist oder all_types gesetzt ist und nicht mehr
1838 // alle Sinne vorhanden sind, wird die Nachricht nicht wahrgenommen.
1839 if (orig_senses == senses // kein Sinn geloescht, haeufigster Fall
1840 || (!(msg_type&MSG_ALL_SENSES) && senses) ) // min. ein Sinn uebrig
1841 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02001842
Bugfix427a5812022-01-07 15:41:24 +01001843 return MSG_SENSE_BLOCK;
MG Mud User88f12472016-06-24 23:31:02 +02001844}
1845
1846public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
1847 string msg_prefix, object origin)
1848{
1849 if (!msg) return MSG_FAILED;
1850
1851 // Flags und Typen spalten
1852 int flags = msg_type & MSG_ALL_FLAGS;
1853 int type = msg_type & ~flags;
1854
1855 // ggf. defaults ermitteln
1856 origin ||= previous_object();
1857 msg_action ||= comm_guess_action();
1858 type ||= comm_guess_message_type(msg_action, origin);
1859
1860 // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
1861 if (msg_type & MT_DEBUG)
1862 {
1863 if (!QueryProp(P_WIZ_DEBUG)
1864 || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
1865 return MSG_FAILED;
1866 }
1867
1868 // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
1869 // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
1870 // Weiterbearbeitung.
1871 // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
1872 if (!(flags & MSG_DONT_IGNORE))
1873 {
Bugfix427a5812022-01-07 15:41:24 +01001874 // Sinne pruefen.
1875 int res=check_senses(msg_type);
MG Mud User88f12472016-06-24 23:31:02 +02001876 if (res) return res;
1877
1878 // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
1879 res=check_ignores(msg, type, msg_action, msg_prefix, origin);
1880 if (res) return res;
1881 }
1882
1883 // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
1884 if ((type & MT_COMM))
1885 {
1886 // erstmal in der Komm-History ablegen, wenn gewuenscht.
1887 if ((!(flags & MSG_DONT_STORE)))
1888 {
1889 string uid;
1890 if (query_once_interactive(origin))
1891 uid = origin->query_real_name();
1892 else
1893 uid = origin->name(WER) || "<unbekannt>";
Zesstraa31cd5c2016-12-17 20:11:07 +01001894 add_to_tell_history(uid, 0, 1, msg, msg_prefix,
1895 (msg_action == MA_TELL ? MSGFLAG_TELL : 0 ) );
MG Mud User88f12472016-06-24 23:31:02 +02001896 }
1897
1898 // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
1899 // Ebenenmeldungen. (Die haben ggf. schon.)
1900 if (stringp(msg_action) && QueryProp(P_AWAY)
1901 && strstr(msg_action, MA_CHANNEL) != 0)
1902 {
1903 // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
1904 if (msg[<1] == '\n')
1905 msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
1906 else
1907 msg = msg + " [" + strftime("%H:%M",time()) + "]";
1908 }
1909 // Kobold erlaubt und gewuenscht? Kobold ist fuer die
1910 // direkte Kommunikation mittels MT_COMM vorgesehen.
1911 // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
1912 // ignorieren.
1913 // die if-Konstruktion ist so, weil ich das _flush_cache() im else
1914 // brauche.
1915 if (query_editing(this_object()) || query_input_pending(this_object())
1916 || QueryProp(P_EARMUFFS))
1917 {
1918 if (!(flags & MSG_DONT_BUFFER)
1919 && QueryProp(P_BUFFER))
1920 {
1921 // Nachricht soll im Kobold gespeichert werden.
1922 return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
1923 }
1924 }
1925 else
1926 {
1927 // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
1928 // entleeren.
1929 _flush_cache(0);
1930 }
1931
1932 // ggf. Piepston anhaengen. NACH Koboldablage, die sollen erstmal keinen
1933 // Pieps kriegen.
1934 if (comm_beep())
Bugfix3afcb792022-01-21 22:32:42 +01001935 flags |= MSG_ALERT;
1936 }
1937
1938 if(flags & MSG_ALERT && !(QueryProp(P_ALERT) & AL_NO_SOUND))
1939 {
1940 msg += MESSAGE_BEEP;
MG Mud User88f12472016-06-24 23:31:02 +02001941 }
1942
1943 // Ausgabenachricht bauen und an den Spieler senden.
1944 if (flags & MSG_DONT_WRAP)
1945 msg = (msg_prefix ? msg_prefix : "") + msg;
1946 else
1947 {
1948 int bsflags = flags & MSG_ALL_BS_FLAGS;
1949 if (QueryProp(P_MESSAGE_PREPEND))
1950 bsflags |= BS_PREPEND_INDENT;
1951 msg = break_string(msg, 78, msg_prefix, bsflags);
1952 }
1953 efun::tell_object(ME, msg);
1954
1955 return MSG_DELIVERED;
1956}