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