blob: 01ecaf4956182b66cb6657166778ab30d2e4a1df [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// channel.c -- channel client
4//
5// $Id: channel.c 9404 2015-12-13 00:21:44Z Zesstra $
6#pragma strong_types
7#pragma save_types
8#pragma range_check
9#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +020010
11#define NEED_PROTOTYPES
12#include <util.h>
13#include <thing/properties.h>
14#include <living/comm.h>
15#include <player.h>
16#include <player/comm.h>
17#include <daemon.h>
18#include <player/gmcp.h>
19#undef NEED_PROTOTYPES
20
21#include <wizlevels.h>
22#include <defines.h>
23#include <properties.h>
24#include <sys_debug.h>
25#include <regexp.h>
26
MG Mud User88f12472016-06-24 23:31:02 +020027#define P_CHANNEL_SHORT "short_channels"
28
29#define CHANNELCMDS "[#@%$&()<>a-zA-Z0-9\\-]"
30
Arathorn78c08372019-12-11 20:14:23 +010031#define DEFAULT_CHANNELS ({"Abenteuer", "Anfaenger", "Grats", "Tod", "ZT"})
MG Mud User88f12472016-06-24 23:31:02 +020032#define DEFAULT_SHORTCUTS \
33([ \
34 "b":"Abenteuer", \
35 "a":"Allgemein", \
36 "B":"Beileid", \
37 "q":"D-chat", \
38 "G":"Grats", \
39 "M":"Moerder", \
40 "h":"Seher", \
41 "T":"Tod", \
42])
43
44#define WIZARD_SHORTCUTS \
45([ \
46 "P":"D-code", \
47 "D":"Debug", \
48 "O":"Intercode", \
49 "I":"Intermud", \
50 "m":"Magier", \
51])
52
Arathorn78c08372019-12-11 20:14:23 +010053// TODO: <shortcut> wird eigentlich nur in getChannel() sinnvoll verwendet
54// Koennte man an sich komplett einsparen und dort wie ueberall sonst auch
55// einfach nur mit P_CHANNEL_SHORT arbeiten.
MG Mud User88f12472016-06-24 23:31:02 +020056private nosave mapping shortcut;
57private nosave int c_status;
58
59void create()
60{
61 Set(P_CHANNELS, SAVE, F_MODE);
62 Set(P_CHANNELS, DEFAULT_CHANNELS);
63 Set(P_SWAP_CHANNELS, SAVE, F_MODE);
64 Set(P_STD_CHANNEL, "Allgemein");
65 Set(P_STD_CHANNEL, SAVE, F_MODE);
66 Set(P_CHANNEL_SHORT, SAVE, F_MODE);
67 Set(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
Arathorn78c08372019-12-11 20:14:23 +010068 + (IS_LEARNER(ME) ? WIZARD_SHORTCUTS : ([])));
MG Mud User88f12472016-06-24 23:31:02 +020069}
70
Arathorn00764692019-11-27 22:09:31 +010071static <int|string>** _query_localcmds()
MG Mud User88f12472016-06-24 23:31:02 +020072{
73 return ({({"-","ChannelParser", 1, 0}),
74 ({"ebene", "ChannelAdmin", 0, 0}),
75 ({"ebenen", "ChannelAdmin", 1, 0}),
76 });
77}
78
Arathorn00764692019-11-27 22:09:31 +010079string* RegisterChannels()
MG Mud User88f12472016-06-24 23:31:02 +020080{
Arathorn85de7602019-11-28 23:04:03 +010081 if (extern_call() &&
82 previous_object() != find_object(CHMASTER))
Zesstraf1137e82019-11-27 23:37:30 +010083 return 0;
84
MG Mud User88f12472016-06-24 23:31:02 +020085 c_status = 0;
86 shortcut = QueryProp(P_CHANNEL_SHORT);
Arathorn85de7602019-11-28 23:04:03 +010087 SetProp(P_CHANNELS, map(QueryProp(P_CHANNELS) || ({}), #'lower_case));
88
89 closure cl = symbol_function("join", CHMASTER);
90 string* err;
91 if (closurep(cl))
92 {
Arathorn78c08372019-12-11 20:14:23 +010093 err = filter(QueryProp(P_CHANNELS), cl, ME);
MG Mud User88f12472016-06-24 23:31:02 +020094 }
Arathorn85de7602019-11-28 23:04:03 +010095 if (QueryProp(P_LEVEL) < 5)
96 return err;
97
98 // CHMASTER->new() gibt bei Erfolg 0 zurueck, d.h. es bleiben
99 // alle Elemente erhalten, deren Channel nicht erstellt werden konnten.
100 // Die werden an die Aufrufer zurueckgegeben.
Arathorn78c08372019-12-11 20:14:23 +0100101 return filter(err, "new", CHMASTER, ME);
MG Mud User88f12472016-06-24 23:31:02 +0200102}
103
Arathorn00764692019-11-27 22:09:31 +0100104string* RemoveChannels()
MG Mud User88f12472016-06-24 23:31:02 +0200105{
Arathorn85de7602019-11-28 23:04:03 +0100106 if (extern_call() &&
107 previous_object() != find_object(CHMASTER))
Zesstraf1137e82019-11-27 23:37:30 +0100108 return 0;
109
Arathorn85de7602019-11-28 23:04:03 +0100110 string* err = ({});
111
112 if (!c_status)
113 c_status = 1;
114 else
115 return err;
116
117 closure cl = symbol_function("leave", CHMASTER);
118 if (closurep(cl))
119 {
Arathorn78c08372019-12-11 20:14:23 +0100120 err = filter(QueryProp(P_CHANNELS), cl, ME);
Arathorn85de7602019-11-28 23:04:03 +0100121 SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - err);
MG Mud User88f12472016-06-24 23:31:02 +0200122 }
123 return err;
124}
125
Arathorn78c08372019-12-11 20:14:23 +0100126varargs private string getName(string|object|closure who, int fall) {
127 // Objekt zur Closure raussuchen. <o> ist danach entweder jenes Objekt,
128 // oder aber <who> selbst, das Objekt oder String sein kann.
129 //
130 // query_closure_object() KANN auch 0 oder -1 zurueckgeben. In beiden
131 // Faellen fuehrt das hier zu einem Laufzeit-Typfehler, weil ein Integer
132 // bzw. die urspruengliche Closure zugewiesen wird. Diesen Fall abzufangen
133 // und separat zu behandeln, hilft nicht, weil es auch keine alternative
134 // Moeglichkeit gibt, <who> in verwendbare Daten umzuwandeln, denn das ist
135 // ja offenbar eine kaputte Closure. Dieser Fall ist ausserdem anscheinend
136 // so exotisch, dass wir vorerst auf Absicherungen mittels catch()
137 // verzichten.
138 string|object o = closurep(who) ? query_closure_object(who) : who;
139
140 // Ist es ein String, pruefen wir, ob es vielleicht ein Objektname ist,
141 // indem wir es in ein Objekt umzuwandeln versuchen. Schlaegt das fehl,
142 // schreiben wir <who> wieder zurueck, so dass <o> wieder der urspruengliche
143 // String ist.
144 if (stringp(o) && sizeof(o))
145 o = find_object(o) || who;
Arathorn85de7602019-11-28 23:04:03 +0100146
MG Mud User88f12472016-06-24 23:31:02 +0200147 // Objekte
Arathorn85de7602019-11-28 23:04:03 +0100148 if (objectp(o))
149 {
MG Mud User88f12472016-06-24 23:31:02 +0200150 // Magier sehen unsichtbare nicht nur als "Jemand"
Arathorn78c08372019-12-11 20:14:23 +0100151 if (o->QueryProp(P_INVIS) && IS_LEARNING(ME))
MG Mud User88f12472016-06-24 23:31:02 +0200152 return "("+capitalize(getuid(o))+")";
153 // Froesche mit Namen versorgen.
154 if (o->QueryProp(P_FROG))
155 return "Frosch "+capitalize(getuid(o));
156 // Default (Unsichtbare als "Jemand" (s. Name()))
157 return o->Name(fall, 2)||"<Unbekannt>";
158 }
159 // Strings
Arathorn85de7602019-11-28 23:04:03 +0100160 else if (stringp(o) && sizeof(o))
161 {
162 if (o[0] == '/')
163 {
MG Mud User88f12472016-06-24 23:31:02 +0200164 // unsichtbare Objekte...
165 int p = strstr(o, "$");
Arathorn85de7602019-11-28 23:04:03 +0100166 if (p != -1)
167 {
168 // Magier im Magiermodus kriegen den Realnamen, andere nicht.
Arathorn78c08372019-12-11 20:14:23 +0100169 if (IS_LEARNING(ME))
Arathorn85de7602019-11-28 23:04:03 +0100170 return o[1..p-1];
171 else
172 return o[p+1..];
MG Mud User88f12472016-06-24 23:31:02 +0200173 }
Arathorn85de7602019-11-28 23:04:03 +0100174 else // doch nicht unsichtbar
175 return (fall == WESSEN ? o+"s" : o);
MG Mud User88f12472016-06-24 23:31:02 +0200176 }
177 else
178 // nicht unsichtbar
179 return (fall == WESSEN ? o+"s" : o);
180 }
181 // Fall-through
182 return "<Unbekannt>";
183}
184
Arathorn69d6ddd2019-11-25 21:06:34 +0100185// <nonint> unterdrueckt die Ausgabe an den Spieler und liefert den Text
Arathorn85de7602019-11-28 23:04:03 +0100186// zurueck. Wird nur fuer die Ebenenhistory benutzt.
Arathorn69d6ddd2019-11-25 21:06:34 +0100187string ChannelMessage(<string|object|int>* msg, int nonint)
MG Mud User88f12472016-06-24 23:31:02 +0200188{
189 string channel_message;
Arathorn85de7602019-11-28 23:04:03 +0100190 string channel = msg[0];
Arathorn69d6ddd2019-11-25 21:06:34 +0100191
192 // Wenn eine Ebenenmeldung ausgegeben werden soll, ist msg[1] ein Objekt,
193 // im Fall der History aber ein String. Daher wird <sender> als Union
194 // deklariert. Das ist unproblematisch, weil die beiden Datentypen
195 // komplett getrennte Wege nehmen: ein Objekt wird an ReceiveMsg()
196 // durchgereicht (Ebenenmeldung). Ein String wird direkt in die Meldung
197 // (History) eingebaut, diese an den ChannelParser() zurueckgegeben, der
198 // sie via More() ausgibt.
Arathorn85de7602019-11-28 23:04:03 +0100199 string|object sender = msg[1];
200 string message = msg[2];
MG Mud User88f12472016-06-24 23:31:02 +0200201 int msg_type = msg[3];
202
Arathorn85de7602019-11-28 23:04:03 +0100203 if (previous_object() != find_object(CHMASTER) &&
204 previous_object() != ME )
205 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200206
207 string sender_name = getName(sender, msg_type == MSG_GEMOTE ? WESSEN : WER);
Arathorn85de7602019-11-28 23:04:03 +0100208 int prepend_indent_flag =
209 QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0;
210
211 switch (msg_type)
212 {
213 case MSG_EMPTY:
214 channel_message= message+"\n";
215 break;
216 case MSG_GEMOTE:
217 case MSG_EMOTE:
218 channel_message = break_string(sender_name + " "+ message+"]", 78,
219 sprintf("[%s:", channel),
220 BS_INDENT_ONCE|prepend_indent_flag);
221 break;
222 case MSG_SAY:
223 default:
Arathorn78c08372019-12-11 20:14:23 +0100224 string presay = sprintf("[%s:%s] ", channel, sender_name);
Arathorn85de7602019-11-28 23:04:03 +0100225 channel_message = break_string(message, max(78,sizeof(presay)+10),
226 presay, prepend_indent_flag);
227 break;
MG Mud User88f12472016-06-24 23:31:02 +0200228 }
Arathorn85de7602019-11-28 23:04:03 +0100229
230 if (nonint)
MG Mud User88f12472016-06-24 23:31:02 +0200231 return channel_message;
232
233 // Wenn GMCP sich um Uebertragung der Nachricht kuemmert, wird ReceiveMsg()
Zesstra7ccec732019-01-06 22:10:09 +0100234 // nicht mehr aufgerufen. getName leider nochmal aufrufen, weil GMCP den
235 // Namen im Nominativ braucht.
236 if (msg_type == MSG_GEMOTE)
237 sender_name = getName(sender, WER);
MG Mud User88f12472016-06-24 23:31:02 +0200238 if (GMCP_Channel(channel_message, channel, sender_name) != 1)
239 {
240 // Der Ebenenname muss in Kleinbuchstaben uebergeben werden, damit die
241 // Ignorierepruefung funktioniert. Die ignorierestrings sind naemlich alle
242 // kleingeschrieben.
Arathorn85de7602019-11-28 23:04:03 +0100243 ReceiveMsg(channel_message,
MG Mud User88f12472016-06-24 23:31:02 +0200244 MT_COMM|MT_FAR|MSG_DONT_STORE|MSG_DONT_WRAP,
245 MA_CHANNEL"." + lower_case(channel), 0, sender);
246 }
247 return 0;
248}
249
MG Mud User88f12472016-06-24 23:31:02 +0200250
Arathorn78c08372019-12-11 20:14:23 +0100251private void createList(mapping ch_list, string* p_channels,
252 int show_only_subscribed) {
253 // Kopfzeile der Tabelle erstellen.
254 string listhead =
255 sprintf("%-12.12' 's [A] %|12' 's (%-3' 's) %-42.42s\n",
256 "Name", "Eigner", "Sp", "Beschreibung");
257 // Linie drunter.
258 listhead += ("-"*78 + "\n");
259
260 // Rest der Daten erstmal in einem Array zusammenstellen, um es
261 // anschliessend sortieren zu koennen.
262 string* entries = ({});
263
264 object* ch_members;
265 string|object ch_master;
266 string ch_real_name, ch_description;
267 closure|string ch_access;
268 closure|string ch_info;
269 string sh;
270
271 foreach(string chname, <object*|closure|string|object>* chdata : ch_list)
272 {
273 // Wenn nur abonnierte Ebenen aufgelistet werden sollen, dann alle
274 // ueberspringen, die nicht in P_CHANNELS stehen.
275 int is_subscribed = (member(p_channels, chname) > -1);
276 if (show_only_subscribed && !is_subscribed)
277 continue;
278
279 ch_members = chdata[I_MEMBER];
280 ch_master = chdata[I_MASTER];
281 ch_access = chdata[I_ACCESS];
282 ch_real_name = chdata[I_NAME];
283 ch_info = chdata[I_INFO];
284 sh = "";
285
286 // Ist eine Closure als I_INFO eingetragen, zu der es auch ein Objekt
287 // gibt, tragen wir deren Rueckgabewert als Beschreibung ein.
288 if (closurep(ch_info) && objectp(query_closure_object(ch_info))) {
289 ch_description = funcall(ch_info);
290 }
291 // Ist es ein String, wird er unveraendert uebernommen.
292 else if (stringp(ch_info)) {
293 ch_description = ch_info;
294 }
295 // Sollte nirgends etwas eingetragen sein, oder die Closure 0 zurueck-
296 // gegeben haben, gibt es eine Defaultbeschreibung. Man haette die
297 // Variable auch schon mit dem Default initialisieren koennen, der kann
298 // aber bei Rueckgabe von 0 aus der Closure wieder ueberschrieben werden.
299 // Daher passiert das erst hier.
300 ch_description ||= "- Keine Beschreibung -";
301
302 // Wir brauchen noch das vom Spieler festgelegte Kuerzel fuer die aktuell
303 // bearbeitete Ebene, falls vorhanden, um es in die Liste eintragen
304 // zu koennen.
305 foreach(string _sh_cut, string _chan_name : shortcut) {
306 if ( lower_case(_chan_name) == chname ) {
307 sh = _sh_cut;
308 break;
309 }
310 }
311
312 // Jetzt haben wir endlich alle Infos beisammen und koennen die
313 // Eintraege zusammenbauen.
314 entries += ({
315 sprintf("%-12.12'.'s %c[%-1.1s] %|12.12' 's (%-|3' 'd) %-42.42s\n",
316 ch_real_name,
317 is_subscribed ? '*' : ' ',
318 sh,
319 ch_master ? getName(ch_master) : getName(ch_access),
320 sizeof(ch_members - ({ find_object(CHMASTER) })),
321 ch_description
322 ) });
323 }
324
325 // alphabetisch sortieren
326 entries = sort_array(entries, #'>);
327
328 More( listhead + // Listenkopf
329 implode(entries, "") + // Eintraege
330 "-"*78+"\n" ); // Linie drunter
331}
332
333private string|string* getChannel(string ch)
MG Mud User88f12472016-06-24 23:31:02 +0200334{
Arathorn85de7602019-11-28 23:04:03 +0100335 if (!sizeof(ch))
Arathorn00764692019-11-27 22:09:31 +0100336 ch = QueryProp(P_STD_CHANNEL);
Arathorn85de7602019-11-28 23:04:03 +0100337 if (shortcut && shortcut[ch])
Arathorn00764692019-11-27 22:09:31 +0100338 ch = shortcut[ch];
Arathorn78c08372019-12-11 20:14:23 +0100339 return CHMASTER->find(ch, ME);
MG Mud User88f12472016-06-24 23:31:02 +0200340}
341
MG Mud User88f12472016-06-24 23:31:02 +0200342int ChannelParser(string args)
343{
Arathorn78c08372019-12-11 20:14:23 +0100344 string|string* ch;
MG Mud User88f12472016-06-24 23:31:02 +0200345 int pos, type, err;
Arathorn78c08372019-12-11 20:14:23 +0100346 string tmp;
MG Mud User88f12472016-06-24 23:31:02 +0200347 notify_fail("Benutzung: -<Ebene>[ ]['|:|;]<Text>\n"
348 " -<Ebene>[+|-|?|!|*]\n"
349 " -?\n");
Arathorn78c08372019-12-11 20:14:23 +0100350
Arathornb794a192020-08-06 21:51:28 +0200351 args = _unparsed_args();
Arathorn78c08372019-12-11 20:14:23 +0100352 string|string* cmd = query_verb();
Arathorn85de7602019-11-28 23:04:03 +0100353 if (!cmd && !args)
354 return 0;
355
356 if (!args)
357 args = "";
358
MG Mud User88f12472016-06-24 23:31:02 +0200359 cmd = cmd[1..];
Arathorn78c08372019-12-11 20:14:23 +0100360 cmd = regexplode(cmd, "^" CHANNELCMDS "*" "([+-]|\\!|\\?|\\*)*");
Arathorn85de7602019-11-28 23:04:03 +0100361 if (sizeof(cmd) > 1)
MG Mud User88f12472016-06-24 23:31:02 +0200362 {
363 //z.B. cmd= ({"","allgemein",":testet"})
Arathorn85de7602019-11-28 23:04:03 +0100364 if (sizeof(cmd[1]) > 1 && strstr("+-?!*", cmd[1][<1..<1]) > -1)
MG Mud User88f12472016-06-24 23:31:02 +0200365 tmp = cmd[1][0..<2];
366 else
367 tmp = cmd[1];
Arathorn85de7602019-11-28 23:04:03 +0100368
369 if (cmd[1] != "?" && cmd[1] != "!")
Zesstra57a693e2019-01-06 22:08:24 +0100370 {
371 ch = getChannel(tmp);
Arathorn85de7602019-11-28 23:04:03 +0100372 if (pointerp(ch))
MG Mud User88f12472016-06-24 23:31:02 +0200373 {
374 notify_fail("Diese Angabe war nicht eindeutig! "
Arathorn78c08372019-12-11 20:14:23 +0100375 "Folgende Ebenen passen:\n"+
376 implode(ch, ", ")+"\n");
MG Mud User88f12472016-06-24 23:31:02 +0200377 return 0;
378 }
Arathorn85de7602019-11-28 23:04:03 +0100379 else if (!ch)
380 {
381 notify_fail("Die Ebene '"+tmp+ "' gibt es nicht!\n");
382 return 0;
383 }
Zesstra57a693e2019-01-06 22:08:24 +0100384 }
Arathorn85de7602019-11-28 23:04:03 +0100385
MG Mud User88f12472016-06-24 23:31:02 +0200386 if (sizeof(cmd[1])) {
Arathorn85de7602019-11-28 23:04:03 +0100387 switch (cmd[1][<1]) {
388 case '+':
Arathorn78c08372019-12-11 20:14:23 +0100389 switch (CHMASTER->join(ch, ME))
Arathorn85de7602019-11-28 23:04:03 +0100390 {
391 case E_ACCESS_DENIED:
392 notify_fail("Du darfst an die Ebene '"+ch+"' nicht heran.\n");
393 return 0;
394 case E_ALREADY_JOINED:
395 notify_fail("Du hast diese Ebene schon betreten!\n");
396 return 0;
397 default:
398 break;
399 }
Arathorn78c08372019-12-11 20:14:23 +0100400 tell_object(ME,"Du betrittst die Ebene '"+ch+"'.\n");
Arathorn85de7602019-11-28 23:04:03 +0100401 if (member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
402 SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
403 return 1;
404
405 case '-':
Arathorn78c08372019-12-11 20:14:23 +0100406 switch (CHMASTER->leave(ch, ME))
Arathorn85de7602019-11-28 23:04:03 +0100407 {
408 case E_ACCESS_DENIED:
Arathorn78c08372019-12-11 20:14:23 +0100409 tell_object(ME,"Du kannst die Ebene '"+ch+"' nicht "
410 "verlassen.\n");
Arathorn85de7602019-11-28 23:04:03 +0100411 break;
412 case E_NOT_MEMBER:
Arathorn78c08372019-12-11 20:14:23 +0100413 tell_object(ME,"Wie willst Du eine Ebene verlassen, welche Du "
414 "nicht betreten hast?\n");
Arathorn85de7602019-11-28 23:04:03 +0100415 break;
416 default:
Arathorn78c08372019-12-11 20:14:23 +0100417 tell_object(ME,"Du verlaesst die Ebene '"+ch+"'.\n");
Arathorn85de7602019-11-28 23:04:03 +0100418 SetProp(P_CHANNELS,
419 QueryProp(P_CHANNELS) - ({ lower_case(ch), ch }));
420 break;
421 }
422 return 1;
423
424 case '!':
425 case '?':
Arathorn78c08372019-12-11 20:14:23 +0100426 mapping l = CHMASTER->list(ME);
427 if (mappingp(l))
Arathorn85de7602019-11-28 23:04:03 +0100428 {
Arathorn78c08372019-12-11 20:14:23 +0100429 // Es wurde ein Channel angegeben, dessen Infos gefragt sind.
Arathorn85de7602019-11-28 23:04:03 +0100430 if (stringp(ch) && sizeof(ch) && pointerp(l[ch = lower_case(ch)]))
431 {
Arathorn78c08372019-12-11 20:14:23 +0100432 <object*|closure|string|object>* chdata = l[ch];
433 string* m =
434 sort_array(map(chdata[I_MEMBER], #'getName, WER), #'>) -
435 ({ CMNAME });
436
437 string wen;
438 switch(sizeof(m)) {
439 case 1: wen = "ein Gesicht"; break;
440 case 0: wen = "niemanden"; break;
441 // TODO: mittels Zahlwoerter-Objekt die Zahl als Text
442 // ausgeben?
443 default: wen = sprintf("%d Gesichter", sizeof(m)); break;
444 }
445
446 tell_object(ME,
447 chdata[I_NAME]+", "+funcall(chdata[I_INFO])+".\n" +
448 "Du siehst "+wen+" auf der Ebene '"+chdata[I_NAME]+"':\n"+
449 break_string(CountUp(m), 78) +
450 (chdata[I_MASTER] ? getName(chdata[I_MASTER])
451 : getName(chdata[I_ACCESS], WER))+
452 " hat das Sagen auf dieser Ebene.\n");
Arathorn85de7602019-11-28 23:04:03 +0100453 }
Arathorn78c08372019-12-11 20:14:23 +0100454 // kein Channel angegeben, dann Gesamtliste erzeugen
Arathorn85de7602019-11-28 23:04:03 +0100455 else
456 {
Arathorn78c08372019-12-11 20:14:23 +0100457 // Wenn nur die abonnierten angezeigt werden sollen
458 // (Kommando -!), wird die Bedingung im 3. Argument == 1, so
459 // dass createList() die reduzierte Tabelle erzeugt.
460 createList(l, QueryProp(P_CHANNELS), (cmd[1][<1] == '!'));
Arathorn85de7602019-11-28 23:04:03 +0100461 }
462 }
463 return 1;
464
465 case '*':
Arathorn78c08372019-12-11 20:14:23 +0100466 int|<int|string>** hist = CHMASTER->history(ch, ME);
Arathorn85de7602019-11-28 23:04:03 +0100467 if (!pointerp(hist) || !sizeof(hist))
468 {
Arathorn78c08372019-12-11 20:14:23 +0100469 tell_object(ME,"Es ist keine Geschichte fuer '"+ch+
470 "' verfuegbar.\n");
Arathorn85de7602019-11-28 23:04:03 +0100471 return 1;
472 }
473
Arathorn85de7602019-11-28 23:04:03 +0100474 int amount = to_int(cmd[2]);
475 if (amount <= 0 || amount >= sizeof(hist))
476 amount = sizeof(hist);
477
Arathorn78c08372019-12-11 20:14:23 +0100478 string txt = "Folgendes ist auf '"+ch+"' passiert:\n" +
479 implode(map(hist[<amount..], #'ChannelMessage, 1), "");
MG Mud User88f12472016-06-24 23:31:02 +0200480 More(txt);
Arathorn85de7602019-11-28 23:04:03 +0100481 return 1;
482
483 default:
484 break;
Zesstra57a693e2019-01-06 22:08:24 +0100485 }
MG Mud User88f12472016-06-24 23:31:02 +0200486 }
487 }
Arathorn85de7602019-11-28 23:04:03 +0100488
Arathorn78c08372019-12-11 20:14:23 +0100489 cmd = implode(cmd[2..], "");
490 if (sizeof(cmd))
Arathorn85de7602019-11-28 23:04:03 +0100491 args = cmd + (sizeof(args) ? " " : "") + args;
MG Mud User88f12472016-06-24 23:31:02 +0200492
493 // KOntrollchars ausfiltern.
Arathorn78c08372019-12-11 20:14:23 +0100494 args = regreplace(args, "[[:cntrl:]]", "", RE_PCRE|RE_GLOBAL);
Arathorn85de7602019-11-28 23:04:03 +0100495 if (!sizeof(args))
496 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200497
498 //Wenn cmd leer ist: MSG_SAY
Arathorn85de7602019-11-28 23:04:03 +0100499 if (!sizeof(cmd))
Arathorn78c08372019-12-11 20:14:23 +0100500 {
Arathorn85de7602019-11-28 23:04:03 +0100501 type = MSG_SAY;
Arathorn78c08372019-12-11 20:14:23 +0100502 }
Arathorn85de7602019-11-28 23:04:03 +0100503 else
504 {
505 switch (cmd[0])
MG Mud User88f12472016-06-24 23:31:02 +0200506 {
Arathorn85de7602019-11-28 23:04:03 +0100507 case ':' :
508 type = MSG_EMOTE;
509 args = args[1..];
510 break;
511 case ';' :
512 type = MSG_GEMOTE;
513 args = args[1..];
514 break;
515 case '\'':
516 args = args[1..];
517 // Der Fallthrough in default ist hier Absicht.
518 default :
519 type = MSG_SAY;
520 break;
MG Mud User88f12472016-06-24 23:31:02 +0200521 }
522 }
Arathorn85de7602019-11-28 23:04:03 +0100523
524 if (!ch || !sizeof(ch))
525 ch = QueryProp(P_STD_CHANNEL);
526
Arathorn78c08372019-12-11 20:14:23 +0100527 err = CHMASTER->send(ch, ME, args, type);
Arathorn85de7602019-11-28 23:04:03 +0100528 if (err < 0)
529 {
Arathorn78c08372019-12-11 20:14:23 +0100530 err = CHMASTER->join(ch, ME);
Arathorn85de7602019-11-28 23:04:03 +0100531 if (!err)
MG Mud User88f12472016-06-24 23:31:02 +0200532 {
Arathorn78c08372019-12-11 20:14:23 +0100533 ch = lower_case(ch);
534 if (member(QueryProp(P_CHANNELS), ch) == -1)
MG Mud User88f12472016-06-24 23:31:02 +0200535 SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
Arathorn78c08372019-12-11 20:14:23 +0100536 err = CHMASTER->send(ch, ME, args, type);
MG Mud User88f12472016-06-24 23:31:02 +0200537 }
Arathorn85de7602019-11-28 23:04:03 +0100538 }
MG Mud User88f12472016-06-24 23:31:02 +0200539
Arathorn85de7602019-11-28 23:04:03 +0100540 switch (err)
MG Mud User88f12472016-06-24 23:31:02 +0200541 {
Arathorn85de7602019-11-28 23:04:03 +0100542 case E_ACCESS_DENIED:
543 notify_fail("Auf der Ebene '"+ch+"' darfst Du nichts sagen.\n");
544 return 0;
545 case E_NOT_MEMBER:
546 notify_fail("Du hast die Ebene '"+ch+"' nicht betreten!\n");
547 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200548 }
549 return 1;
550}
551
552int ChannelAdmin(string args)
553{
Arathorn85de7602019-11-28 23:04:03 +0100554 args = _unparsed_args();
555
Arathorn78c08372019-12-11 20:14:23 +0100556 string target_channel, descr, _sh_cut;
557 string|string* chans;
MG Mud User88f12472016-06-24 23:31:02 +0200558 notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
559 " ebene <Abkuerzung>=\n"
560 " ebene abkuerzungen [standard]\n"
561 " ebene standard <Ebene>\n"
562 " ebene an|ein|aus\n"
563 +(QueryProp(P_LEVEL) >= 5 ?
564 " ebene neu <Name> <Bezeichnung>\n"
Arathorn85de7602019-11-28 23:04:03 +0100565 " ebene beschreibung <Name> <Beschreibung>\n" : "")
Arathorn78c08372019-12-11 20:14:23 +0100566 +(IS_ARCH(ME) ?
Arathorn85de7602019-11-28 23:04:03 +0100567 " ebene kill <Name>\n"
568 " ebene clear <Name>\n": ""));
569
570 if (!args || !sizeof(args))
571 return 0;
572
Arathorn78c08372019-12-11 20:14:23 +0100573 if (sscanf(args, "kill %s", target_channel) && IS_ARCH(ME))
MG Mud User88f12472016-06-24 23:31:02 +0200574 {
Arathorn78c08372019-12-11 20:14:23 +0100575 chans = CHMASTER->find(target_channel, ME);
Arathorn85de7602019-11-28 23:04:03 +0100576
Arathorn78c08372019-12-11 20:14:23 +0100577 if (!chans)
578 {
579 notify_fail("Ebene '"+target_channel+"' nicht gefunden!\n");
Arathorn85de7602019-11-28 23:04:03 +0100580 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200581 }
Arathorn78c08372019-12-11 20:14:23 +0100582 else if (pointerp(chans))
583 {
584 notify_fail(
585 "Das war keine eindeutige Angabe! Folgende Ebenen passen:\n"+
586 break_string(CountUp(chans), 78));
587 return 0;
588 }
589 else if (CHMASTER->remove_channel(target_channel, ME))
590 {
591 notify_fail("Die Ebene '"+target_channel+"' lies sich nicht "
Arathorn85de7602019-11-28 23:04:03 +0100592 "entfernen!\n");
593 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200594 }
Arathorn85de7602019-11-28 23:04:03 +0100595
Arathorn78c08372019-12-11 20:14:23 +0100596 tell_object(ME,"Du entfernst die Ebene '"+target_channel+"'.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200597 return 1;
598 }
Arathorn85de7602019-11-28 23:04:03 +0100599
Arathorn78c08372019-12-11 20:14:23 +0100600 if (sscanf(args, "clear %s", target_channel) && IS_ARCH(ME))
MG Mud User88f12472016-06-24 23:31:02 +0200601 {
Arathorn78c08372019-12-11 20:14:23 +0100602 chans = CHMASTER->find(target_channel, ME);
603
604 if (!chans)
605 {
606 notify_fail("Ebene '"+target_channel+"' nicht gefunden!\n");
607 return 0;
608 }
609 else if (pointerp(chans))
610 {
611 notify_fail(
612 "Das war keine eindeutige Angabe! Folgende Ebenen passen:\n"+
613 break_string(CountUp(chans), 78));
614 return 0;
615 }
616 else if (CHMASTER->clear_history(target_channel, ME) == E_ACCESS_DENIED)
617 {
618 notify_fail("Der Verlauf zur Ebene '"+target_channel+"' liess sich "
619 "nicht entfernen!\n");
620 return 0;
621 }
622
623 tell_object(ME,"Du entfernst den Verlauf zur Ebene '"+target_channel+
624 "'.\n");
625 return 1;
626 }
627
628 if (sscanf(args, "neu %s %s", target_channel, descr) == 2)
629 {
630 notify_fail(break_string("Neue Ebenen kannst Du erst erstellen, wenn "
631 "Du Spielerstufe 5 erreicht hast.", 78));
Arathorn85de7602019-11-28 23:04:03 +0100632 if (QueryProp(P_LEVEL) < 5)
Arathorn85de7602019-11-28 23:04:03 +0100633 return 0;
Arathorn85de7602019-11-28 23:04:03 +0100634
Arathorn78c08372019-12-11 20:14:23 +0100635 notify_fail("Der Name '"+target_channel+"' ist nicht konform!\n");
636 if (!sizeof(regexp(({target_channel}), "^" CHANNELCMDS CHANNELCMDS "*")))
Arathorn85de7602019-11-28 23:04:03 +0100637 return 0;
Arathorn85de7602019-11-28 23:04:03 +0100638
Arathorn78c08372019-12-11 20:14:23 +0100639 notify_fail("Der Name '"+target_channel+"' ist zu lang.\n");
640 if (sizeof(target_channel) > 20)
Arathorn85de7602019-11-28 23:04:03 +0100641 return 0;
Arathorn85de7602019-11-28 23:04:03 +0100642
Arathorn78c08372019-12-11 20:14:23 +0100643 notify_fail("Diese Ebene darfst du nicht erschaffen!\n");
644 if (CHMASTER->new(target_channel, ME, descr) == E_ACCESS_DENIED)
Arathorn85de7602019-11-28 23:04:03 +0100645 return 0;
Arathorn78c08372019-12-11 20:14:23 +0100646
647 tell_object(ME,"Du erschaffst die Ebene '"+target_channel+"'.\n");
648 SetProp(P_CHANNELS, QueryProp(P_CHANNELS) +
649 ({lower_case(target_channel)}));
Arathorn85de7602019-11-28 23:04:03 +0100650 return 1;
MG Mud User88f12472016-06-24 23:31:02 +0200651 }
Arathorn85de7602019-11-28 23:04:03 +0100652
Arathorn78c08372019-12-11 20:14:23 +0100653 if (sscanf(args, "beschreibung %s %s", target_channel, descr) == 2)
MG Mud User88f12472016-06-24 23:31:02 +0200654 {
Arathorn78c08372019-12-11 20:14:23 +0100655 chans = CHMASTER->find(target_channel, ME);
656
657 if (!chans)
Arathorn85de7602019-11-28 23:04:03 +0100658 {
Arathorn78c08372019-12-11 20:14:23 +0100659 notify_fail("Ebene '"+target_channel+"' nicht gefunden!\n");
660 return 0;
661 }
662 else if (pointerp(chans))
663 {
664 notify_fail(
665 "Das war keine eindeutige Angabe! Folgende Ebenen passen:\n"+
666 break_string(CountUp(chans), 78));
Arathorn85de7602019-11-28 23:04:03 +0100667 return 0;
668 }
669
Arathorn78c08372019-12-11 20:14:23 +0100670 mapping ch = CHMASTER->list(ME);
671 notify_fail("Du bist nicht berechtigt, die Beschreibung der Ebene"
672 " '"+target_channel+"' zu aendern.\n");
673 if (ch[lower_case(chans)][I_MASTER] != ME)
Arathorn85de7602019-11-28 23:04:03 +0100674 return 0;
Arathorn78c08372019-12-11 20:14:23 +0100675
676 ch[lower_case(target_channel)][I_INFO] = descr;
677 tell_object(ME,"Die Ebene '"+target_channel+"' hat ab sofort die "
678 "Beschreibung:\n"+descr+"\n");
MG Mud User88f12472016-06-24 23:31:02 +0200679 return 1;
680 }
Arathorn85de7602019-11-28 23:04:03 +0100681
Arathorn78c08372019-12-11 20:14:23 +0100682 if (sscanf(args, "%s=%s", _sh_cut, target_channel) == 2 &&
683 sizeof(target_channel))
MG Mud User88f12472016-06-24 23:31:02 +0200684 {
Arathorn78c08372019-12-11 20:14:23 +0100685 chans = CHMASTER->find(target_channel, ME);
686
687 if (!chans)
Arathorn85de7602019-11-28 23:04:03 +0100688 {
Arathorn78c08372019-12-11 20:14:23 +0100689 notify_fail("Ebene '"+target_channel+"' nicht gefunden!\n");
690 return 0;
691 }
692 else if (pointerp(chans))
693 {
694 notify_fail(
695 "Das war keine eindeutige Angabe! Folgende Ebenen passen:\n"+
696 break_string(CountUp(chans), 78));
Arathorn85de7602019-11-28 23:04:03 +0100697 return 0;
698 }
699
Arathorn78c08372019-12-11 20:14:23 +0100700 mapping shortcut_list = QueryProp(P_CHANNEL_SHORT) || ([]);
701 m_add(shortcut_list, _sh_cut, chans);
702 SetProp(P_CHANNEL_SHORT, shortcut_list);
Arathorn85de7602019-11-28 23:04:03 +0100703
MG Mud User88f12472016-06-24 23:31:02 +0200704 shortcut = QueryProp(P_CHANNEL_SHORT);
Arathorn85de7602019-11-28 23:04:03 +0100705
Arathorn78c08372019-12-11 20:14:23 +0100706 tell_object(ME,"'"+_sh_cut+"' wird jetzt als Abkuerzung fuer '"+chans+
707 "' anerkannt.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200708 return 1;
709 }
Arathorn85de7602019-11-28 23:04:03 +0100710
Arathorn78c08372019-12-11 20:14:23 +0100711 if (sscanf(args, "%s=", _sh_cut))
MG Mud User88f12472016-06-24 23:31:02 +0200712 {
Arathorn85de7602019-11-28 23:04:03 +0100713 SetProp(P_CHANNEL_SHORT,
Arathorn78c08372019-12-11 20:14:23 +0100714 m_delete(QueryProp(P_CHANNEL_SHORT) || ([]), _sh_cut));
MG Mud User88f12472016-06-24 23:31:02 +0200715 shortcut = QueryProp(P_CHANNEL_SHORT);
Arathorn78c08372019-12-11 20:14:23 +0100716 tell_object(ME,"Du loeschst die Abkuerzung '"+_sh_cut+"'.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200717 return 1;
718 }
Arathorn85de7602019-11-28 23:04:03 +0100719
720 if (args == "an" || args == "ein")
MG Mud User88f12472016-06-24 23:31:02 +0200721 {
Arathorn85de7602019-11-28 23:04:03 +0100722 if (pointerp(QueryProp(P_SWAP_CHANNELS)))
MG Mud User88f12472016-06-24 23:31:02 +0200723 SetProp(P_CHANNELS, QueryProp(P_SWAP_CHANNELS));
724 else
Arathorn78c08372019-12-11 20:14:23 +0100725 SetProp(P_CHANNELS, m_indices(CHMASTER->list(ME)));
Arathorn85de7602019-11-28 23:04:03 +0100726
727 // <excl> enthaelt die Channelnamen, deren Channel nicht erstellt wurden.
728 string* excl = RegisterChannels();
Arathorn78c08372019-12-11 20:14:23 +0100729 tell_object(ME,"Du schaltest folgende Ebenen ein:\n"+
730 break_string(CountUp(QueryProp(P_CHANNELS) - excl), 78));
MG Mud User88f12472016-06-24 23:31:02 +0200731 SetProp(P_SWAP_CHANNELS, 0);
732 return 1;
733 }
Arathorn85de7602019-11-28 23:04:03 +0100734
735 if (args == "aus")
MG Mud User88f12472016-06-24 23:31:02 +0200736 {
737 SetProp(P_SWAP_CHANNELS, QueryProp(P_CHANNELS));
738 RemoveChannels();
739 SetProp(P_CHANNELS, ({}));
Arathorn78c08372019-12-11 20:14:23 +0100740 tell_object(ME,"Du stellst die Ebenen ab.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200741 return 1;
742 }
Arathorn85de7602019-11-28 23:04:03 +0100743
Arathorn00764692019-11-27 22:09:31 +0100744 string* pa = old_explode(args, " ");
Arathorn78c08372019-12-11 20:14:23 +0100745 if (strstr("abkuerzungen", pa[0]) == 0)
MG Mud User88f12472016-06-24 23:31:02 +0200746 {
Arathorn85de7602019-11-28 23:04:03 +0100747 string txt = "";
Arathorn78c08372019-12-11 20:14:23 +0100748 if (sizeof(pa) > 1 && strstr("standard", pa[1]) == 0)
MG Mud User88f12472016-06-24 23:31:02 +0200749 {
Arathorn78c08372019-12-11 20:14:23 +0100750 tell_object(ME,"Die Standardabkuerzungen werden gesetzt.\n");
751 SetProp(P_CHANNEL_SHORT,
752 DEFAULT_SHORTCUTS + (IS_LEARNER(ME) ? WIZARD_SHORTCUTS : ([])));
MG Mud User88f12472016-06-24 23:31:02 +0200753 }
Arathorn78c08372019-12-11 20:14:23 +0100754
755 mapping scut = QueryProp(P_CHANNEL_SHORT);
756 string* abbreviations = m_indices(scut);
757 foreach (string abk : sort_array(abbreviations, #'>))
758 {
759 txt += sprintf("%5.5s = %s\n", abk, scut[abk]);
Arathorndc28afc2018-11-26 22:20:59 +0100760 }
Arathorn78c08372019-12-11 20:14:23 +0100761 More( "Folgende Abkuerzungen sind definiert:\n"+
762 sprintf("%-78#s\n", txt) );
MG Mud User88f12472016-06-24 23:31:02 +0200763 return 1;
764 }
Arathorn85de7602019-11-28 23:04:03 +0100765
Arathorn78c08372019-12-11 20:14:23 +0100766 if (strstr("standard", pa[0]) == 0)
Arathorn85de7602019-11-28 23:04:03 +0100767 {
768 if (sizeof(pa) < 2)
769 {
Arathorn78c08372019-12-11 20:14:23 +0100770 notify_fail("Benutzung: ebene standard <Ebene>\n"+
771 (QueryProp(P_STD_CHANNEL)
772 ? "Momentan ist '"+QueryProp(P_STD_CHANNEL)+"' eingestellt.\n"
773 : "Es ist keine Standardebene eingestellt.\n"));
Arathorn85de7602019-11-28 23:04:03 +0100774 return 0;
775 }
Arathorn78c08372019-12-11 20:14:23 +0100776
777 chans = CHMASTER->find(pa[1], ME);
778 if (!chans)
Arathorn85de7602019-11-28 23:04:03 +0100779 {
Arathorn78c08372019-12-11 20:14:23 +0100780 notify_fail("Ebene '"+pa[1]+"' nicht gefunden!\n");
781 return 0;
Arathorn85de7602019-11-28 23:04:03 +0100782 }
Arathorn78c08372019-12-11 20:14:23 +0100783 else if (pointerp(chans))
784 {
785 notify_fail(
786 "Das war keine eindeutige Angabe! Folgende Ebenen passen:\n"+
787 break_string(CountUp(chans), 78));
788 return 0;
789 }
790
791 tell_object(ME,"'"+chans+"' ist jetzt die Standardebene.\n");
792 SetProp(P_STD_CHANNEL, chans);
793 return 1;
Arathorn85de7602019-11-28 23:04:03 +0100794 }
MG Mud User88f12472016-06-24 23:31:02 +0200795 return(0);
796}