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