blob: 0078ce93e89de475b3b53069fa3096aef8fb96dd [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
27#define P_SWAP_CHANNELS "swap_channels"
28#define P_CHANNEL_SHORT "short_channels"
29
30#define CHANNELCMDS "[#@%$&()<>a-zA-Z0-9\\-]"
31
32#define DEFAULT_CHANNELS ({"Abenteuer", "Anfaenger","Grats","Tod", "ZT"})
33#define DEFAULT_SHORTCUTS \
34([ \
35 "b":"Abenteuer", \
36 "a":"Allgemein", \
37 "B":"Beileid", \
38 "q":"D-chat", \
39 "G":"Grats", \
40 "M":"Moerder", \
41 "h":"Seher", \
42 "T":"Tod", \
43])
44
45#define WIZARD_SHORTCUTS \
46([ \
47 "P":"D-code", \
48 "D":"Debug", \
49 "O":"Intercode", \
50 "I":"Intermud", \
51 "m":"Magier", \
52])
53
54
55private nosave mapping shortcut;
56private nosave int c_status;
57
58void create()
59{
60 Set(P_CHANNELS, SAVE, F_MODE);
61 Set(P_CHANNELS, DEFAULT_CHANNELS);
62 Set(P_SWAP_CHANNELS, SAVE, F_MODE);
63 Set(P_STD_CHANNEL, "Allgemein");
64 Set(P_STD_CHANNEL, SAVE, F_MODE);
65 Set(P_CHANNEL_SHORT, SAVE, F_MODE);
66 Set(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
67 + (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
68}
69
Arathorn00764692019-11-27 22:09:31 +010070static <int|string>** _query_localcmds()
MG Mud User88f12472016-06-24 23:31:02 +020071{
72 return ({({"-","ChannelParser", 1, 0}),
73 ({"ebene", "ChannelAdmin", 0, 0}),
74 ({"ebenen", "ChannelAdmin", 1, 0}),
75 });
76}
77
Arathorn00764692019-11-27 22:09:31 +010078string* RegisterChannels()
MG Mud User88f12472016-06-24 23:31:02 +020079{
Arathorn85de7602019-11-28 23:04:03 +010080 if (extern_call() &&
81 previous_object() != find_object(CHMASTER))
Zesstraf1137e82019-11-27 23:37:30 +010082 return 0;
83
MG Mud User88f12472016-06-24 23:31:02 +020084 c_status = 0;
85 shortcut = QueryProp(P_CHANNEL_SHORT);
Arathorn85de7602019-11-28 23:04:03 +010086 SetProp(P_CHANNELS, map(QueryProp(P_CHANNELS) || ({}), #'lower_case));
87
88 closure cl = symbol_function("join", CHMASTER);
89 string* err;
90 if (closurep(cl))
91 {
92 err = filter(QueryProp(P_CHANNELS), cl, this_object());
MG Mud User88f12472016-06-24 23:31:02 +020093 }
Arathorn85de7602019-11-28 23:04:03 +010094 if (QueryProp(P_LEVEL) < 5)
95 return err;
96
97 // CHMASTER->new() gibt bei Erfolg 0 zurueck, d.h. es bleiben
98 // alle Elemente erhalten, deren Channel nicht erstellt werden konnten.
99 // Die werden an die Aufrufer zurueckgegeben.
100 return filter(err, CHMASTER, "new", this_object());
MG Mud User88f12472016-06-24 23:31:02 +0200101}
102
Arathorn00764692019-11-27 22:09:31 +0100103string* RemoveChannels()
MG Mud User88f12472016-06-24 23:31:02 +0200104{
Arathorn85de7602019-11-28 23:04:03 +0100105 if (extern_call() &&
106 previous_object() != find_object(CHMASTER))
Zesstraf1137e82019-11-27 23:37:30 +0100107 return 0;
108
Arathorn85de7602019-11-28 23:04:03 +0100109 string* err = ({});
110
111 if (!c_status)
112 c_status = 1;
113 else
114 return err;
115
116 closure cl = symbol_function("leave", CHMASTER);
117 if (closurep(cl))
118 {
119 err = filter(QueryProp(P_CHANNELS), cl, this_object());
120 SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - err);
MG Mud User88f12472016-06-24 23:31:02 +0200121 }
122 return err;
123}
124
Arathorn00764692019-11-27 22:09:31 +0100125varargs private string getName(string|object|closure x, int fall) {
126 string|object o = closurep(x) ? query_closure_object(x) : x;
Arathorn85de7602019-11-28 23:04:03 +0100127 if (stringp(o) && sizeof(o) && (x = find_object(o)))
MG Mud User88f12472016-06-24 23:31:02 +0200128 o = x;
Arathorn85de7602019-11-28 23:04:03 +0100129
MG Mud User88f12472016-06-24 23:31:02 +0200130 // Objekte
Arathorn85de7602019-11-28 23:04:03 +0100131 if (objectp(o))
132 {
MG Mud User88f12472016-06-24 23:31:02 +0200133 // Magier sehen unsichtbare nicht nur als "Jemand"
134 if (o->QueryProp(P_INVIS) && IS_LEARNING(this_object()))
135 return "("+capitalize(getuid(o))+")";
136 // Froesche mit Namen versorgen.
137 if (o->QueryProp(P_FROG))
138 return "Frosch "+capitalize(getuid(o));
139 // Default (Unsichtbare als "Jemand" (s. Name()))
140 return o->Name(fall, 2)||"<Unbekannt>";
141 }
142 // Strings
Arathorn85de7602019-11-28 23:04:03 +0100143 else if (stringp(o) && sizeof(o))
144 {
145 if (o[0] == '/')
146 {
MG Mud User88f12472016-06-24 23:31:02 +0200147 // unsichtbare Objekte...
148 int p = strstr(o, "$");
Arathorn85de7602019-11-28 23:04:03 +0100149 if (p != -1)
150 {
151 // Magier im Magiermodus kriegen den Realnamen, andere nicht.
152 if (IS_LEARNING(this_object()))
153 return o[1..p-1];
154 else
155 return o[p+1..];
MG Mud User88f12472016-06-24 23:31:02 +0200156 }
Arathorn85de7602019-11-28 23:04:03 +0100157 else // doch nicht unsichtbar
158 return (fall == WESSEN ? o+"s" : o);
MG Mud User88f12472016-06-24 23:31:02 +0200159 }
160 else
161 // nicht unsichtbar
162 return (fall == WESSEN ? o+"s" : o);
163 }
164 // Fall-through
165 return "<Unbekannt>";
166}
167
Arathorn69d6ddd2019-11-25 21:06:34 +0100168// <nonint> unterdrueckt die Ausgabe an den Spieler und liefert den Text
Arathorn85de7602019-11-28 23:04:03 +0100169// zurueck. Wird nur fuer die Ebenenhistory benutzt.
Arathorn69d6ddd2019-11-25 21:06:34 +0100170string ChannelMessage(<string|object|int>* msg, int nonint)
MG Mud User88f12472016-06-24 23:31:02 +0200171{
172 string channel_message;
Arathorn85de7602019-11-28 23:04:03 +0100173 string channel = msg[0];
Arathorn69d6ddd2019-11-25 21:06:34 +0100174
175 // Wenn eine Ebenenmeldung ausgegeben werden soll, ist msg[1] ein Objekt,
176 // im Fall der History aber ein String. Daher wird <sender> als Union
177 // deklariert. Das ist unproblematisch, weil die beiden Datentypen
178 // komplett getrennte Wege nehmen: ein Objekt wird an ReceiveMsg()
179 // durchgereicht (Ebenenmeldung). Ein String wird direkt in die Meldung
180 // (History) eingebaut, diese an den ChannelParser() zurueckgegeben, der
181 // sie via More() ausgibt.
Arathorn85de7602019-11-28 23:04:03 +0100182 string|object sender = msg[1];
183 string message = msg[2];
MG Mud User88f12472016-06-24 23:31:02 +0200184 int msg_type = msg[3];
185
Arathorn85de7602019-11-28 23:04:03 +0100186 if (previous_object() != find_object(CHMASTER) &&
187 previous_object() != ME )
188 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200189
190 string sender_name = getName(sender, msg_type == MSG_GEMOTE ? WESSEN : WER);
Arathorn85de7602019-11-28 23:04:03 +0100191 int prepend_indent_flag =
192 QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0;
193
194 switch (msg_type)
195 {
196 case MSG_EMPTY:
197 channel_message= message+"\n";
198 break;
199 case MSG_GEMOTE:
200 case MSG_EMOTE:
201 channel_message = break_string(sender_name + " "+ message+"]", 78,
202 sprintf("[%s:", channel),
203 BS_INDENT_ONCE|prepend_indent_flag);
204 break;
205 case MSG_SAY:
206 default:
207 string presay=sprintf("[%s:%s] ", channel, sender_name);
208 channel_message = break_string(message, max(78,sizeof(presay)+10),
209 presay, prepend_indent_flag);
210 break;
MG Mud User88f12472016-06-24 23:31:02 +0200211 }
Arathorn85de7602019-11-28 23:04:03 +0100212
213 if (nonint)
MG Mud User88f12472016-06-24 23:31:02 +0200214 return channel_message;
215
216 // Wenn GMCP sich um Uebertragung der Nachricht kuemmert, wird ReceiveMsg()
Zesstra7ccec732019-01-06 22:10:09 +0100217 // nicht mehr aufgerufen. getName leider nochmal aufrufen, weil GMCP den
218 // Namen im Nominativ braucht.
219 if (msg_type == MSG_GEMOTE)
220 sender_name = getName(sender, WER);
MG Mud User88f12472016-06-24 23:31:02 +0200221 if (GMCP_Channel(channel_message, channel, sender_name) != 1)
222 {
223 // Der Ebenenname muss in Kleinbuchstaben uebergeben werden, damit die
224 // Ignorierepruefung funktioniert. Die ignorierestrings sind naemlich alle
225 // kleingeschrieben.
Arathorn85de7602019-11-28 23:04:03 +0100226 ReceiveMsg(channel_message,
MG Mud User88f12472016-06-24 23:31:02 +0200227 MT_COMM|MT_FAR|MSG_DONT_STORE|MSG_DONT_WRAP,
228 MA_CHANNEL"." + lower_case(channel), 0, sender);
229 }
230 return 0;
231}
232
233private void createList(string n, mixed a, mixed m, mixed l)
234{
Arathorn00764692019-11-27 22:09:31 +0100235 int pos = member(map(m_values(shortcut), #'lower_case/*'*/), n);
236 string sh = "";
Arathorn85de7602019-11-28 23:04:03 +0100237 if (pos != -1)
MG Mud User88f12472016-06-24 23:31:02 +0200238 sh = m_indices(shortcut)[pos];
Arathorn00764692019-11-27 22:09:31 +0100239
240 string* mem=map(a[I_MEMBER],#'getName/*'*/, WER);
Arathorn85de7602019-11-28 23:04:03 +0100241 mem -= ({"<MasteR>"});
MG Mud User88f12472016-06-24 23:31:02 +0200242 l += ({ sprintf("%-12.12'.'s %c[%-1.1s] %|12.12' 's (%-|3' 'd) %-42.42s\n",
Arathorn85de7602019-11-28 23:04:03 +0100243 a[I_NAME], (member(m, n) != -1 ? '*' : ' '), sh,
244 a[I_MASTER] ?
245 getName(a[I_MASTER]) : getName(a[I_ACCESS]),
246 sizeof(mem),
247 (closurep(a[I_INFO]) && objectp(query_closure_object(a[I_INFO]))) ?
248 funcall(a[I_INFO]) || "- Keine Beschreibung -" :
249 (stringp(a[I_INFO]) ? a[I_INFO] : "- Keine Beschreibung -")
250 ) });
MG Mud User88f12472016-06-24 23:31:02 +0200251}
252
253private mixed getChannel(string ch)
254{
Arathorn85de7602019-11-28 23:04:03 +0100255 if (!sizeof(ch))
Arathorn00764692019-11-27 22:09:31 +0100256 ch = QueryProp(P_STD_CHANNEL);
Arathorn85de7602019-11-28 23:04:03 +0100257 if (shortcut && shortcut[ch])
Arathorn00764692019-11-27 22:09:31 +0100258 ch = shortcut[ch];
MG Mud User88f12472016-06-24 23:31:02 +0200259 return CHMASTER->find(ch, this_object());
260}
261
262#ifndef DEBUG
Arathorn85de7602019-11-28 23:04:03 +0100263#define DEBUG(x) if (funcall(symbol_function('find_player),"zesstra"))\
MG Mud User88f12472016-06-24 23:31:02 +0200264 tell_object(funcall(symbol_function('find_player),"zesstra"),\
Zesstra57a693e2019-01-06 22:08:24 +0100265 "MDBG: "+x+"\n")
MG Mud User88f12472016-06-24 23:31:02 +0200266#endif
Arathorn85de7602019-11-28 23:04:03 +0100267
MG Mud User88f12472016-06-24 23:31:02 +0200268int ChannelParser(string args)
269{
Arathorn6d465642019-11-25 21:06:07 +0100270 mixed ch;
MG Mud User88f12472016-06-24 23:31:02 +0200271 int pos, type, err;
Arathorn6d465642019-11-25 21:06:07 +0100272 string txt, tmp;
273 string|string* cmd = query_verb();
MG Mud User88f12472016-06-24 23:31:02 +0200274 args = _unparsed_args();
275 notify_fail("Benutzung: -<Ebene>[ ]['|:|;]<Text>\n"
276 " -<Ebene>[+|-|?|!|*]\n"
277 " -?\n");
Arathorn85de7602019-11-28 23:04:03 +0100278 if (!cmd && !args)
279 return 0;
280
281 if (!args)
282 args = "";
283
MG Mud User88f12472016-06-24 23:31:02 +0200284 cmd = cmd[1..];
Arathorn85de7602019-11-28 23:04:03 +0100285 cmd = regexplode(cmd, "^" CHANNELCMDS "*" "([+-]|\\!|\\?|\\*)*")
286 if (sizeof(cmd) > 1)
MG Mud User88f12472016-06-24 23:31:02 +0200287 {
288 //z.B. cmd= ({"","allgemein",":testet"})
Arathorn85de7602019-11-28 23:04:03 +0100289 if (sizeof(cmd[1]) > 1 && strstr("+-?!*", cmd[1][<1..<1]) > -1)
MG Mud User88f12472016-06-24 23:31:02 +0200290 tmp = cmd[1][0..<2];
291 else
292 tmp = cmd[1];
Arathorn85de7602019-11-28 23:04:03 +0100293
294 if (cmd[1] != "?" && cmd[1] != "!")
Zesstra57a693e2019-01-06 22:08:24 +0100295 {
296 ch = getChannel(tmp);
Arathorn85de7602019-11-28 23:04:03 +0100297 if (pointerp(ch))
MG Mud User88f12472016-06-24 23:31:02 +0200298 {
299 notify_fail("Diese Angabe war nicht eindeutig! "
300 "Folgende Ebenen passen:\n"
301 +implode(ch, ", ")+"\n");
302 return 0;
303 }
Arathorn85de7602019-11-28 23:04:03 +0100304 else if (!ch)
305 {
306 notify_fail("Die Ebene '"+tmp+ "' gibt es nicht!\n");
307 return 0;
308 }
Zesstra57a693e2019-01-06 22:08:24 +0100309 }
Arathorn85de7602019-11-28 23:04:03 +0100310
MG Mud User88f12472016-06-24 23:31:02 +0200311 if (sizeof(cmd[1])) {
Arathorn85de7602019-11-28 23:04:03 +0100312 switch (cmd[1][<1]) {
313 case '+':
314 switch (CHMASTER->join(ch, this_object()))
315 {
316 case E_ACCESS_DENIED:
317 notify_fail("Du darfst an die Ebene '"+ch+"' nicht heran.\n");
318 return 0;
319 case E_ALREADY_JOINED:
320 notify_fail("Du hast diese Ebene schon betreten!\n");
321 return 0;
322 default:
323 break;
324 }
325 write("Du betrittst die Ebene '"+ch+"'.\n");
326 if (member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
327 SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
328 return 1;
329
330 case '-':
331 switch (CHMASTER->leave(ch, this_object()))
332 {
333 case E_ACCESS_DENIED:
334 write("Du kannst die Ebene '"+ch+"' nicht verlassen.\n");
335 break;
336 case E_NOT_MEMBER:
337 write("Wie willst Du eine Ebene verlassen, welche Du nicht "
338 "betreten hast?\n");
339 break;
340 default:
341 write("Du verlaesst die Ebene '"+ch+"'.\n");
342 SetProp(P_CHANNELS,
343 QueryProp(P_CHANNELS) - ({ lower_case(ch), ch }));
344 break;
345 }
346 return 1;
347
348 case '!':
349 case '?':
350 mapping l;
351 if (mappingp(l = CHMASTER->list(this_object())))
352 {
353 if (stringp(ch) && sizeof(ch) && pointerp(l[ch = lower_case(ch)]))
354 {
355 int c; object o; string n; string *m;
356 m = sort_array(
357 map(l[ch][I_MEMBER],#'getName/*'*/, WER),
358 #'>/*'*/);
359 m-=({"<MasteR>"});
360 write(l[ch][I_NAME]+", "+funcall(l[ch][I_INFO])+".\n");
361 write("Du siehst "+((c = sizeof(m)) > 0
362 ? (c == 1 ? "ein Gesicht" : c+" Gesichter")
363 : "niemanden")+" auf der Ebene '"
364 +l[ch][I_NAME]+"':\n");
365 write(break_string(implode(m,", "), 78));
366 write((l[ch][I_MASTER] ?
367 getName(l[ch][I_MASTER]) : getName(l[ch][I_ACCESS], WER))
368 +" hat das Sagen auf dieser Ebene.\n");
369 }
370 else
371 {
372 string* list = ({});
373 if (cmd[1][<1] == '!')
374 l -= mkmapping(m_indices(l) - QueryProp(P_CHANNELS));
375 walk_mapping(l, #'createList/*'*/, QueryProp(P_CHANNELS), &list);
376 list = sort_array(list, #'>/*'*/);
377 txt = sprintf("%-12.12' 's [A] %|12' 's (%-3' 's) %-42.42s\n",
378 "Name", "Eigner", "Sp", "Beschreibung")
379 + "-------------------------------------------------------"
380 + "-----------------------\n"
381 + implode(list, "");
382 More(txt);
383 }
384 }
385 return 1;
386
387 case '*':
388 mixed hist = CHMASTER->history(ch, this_object());
389 if (!pointerp(hist) || !sizeof(hist))
390 {
391 write("Es ist keine Geschichte fuer '"+ch+"' verfuegbar.\n");
392 return 1;
393 }
394
395 //(Zesstra) cmd hat offenbar immer 3 Elemente...
396 //bei -all* ({"","all*",""})
397 //bei -all*10 ({"","all*,"10"})
398 //also ist bei -all* amount immer == 0 und es funktioniert eher
399 //zufaellig.
400 /*if(sizeof(cmd) > 2)
401 amount = to_int(cmd[2]);
402 else
403 amount=sizeof(hist);*/
404 int amount = to_int(cmd[2]);
405 if (amount <= 0 || amount >= sizeof(hist))
406 amount = sizeof(hist);
407
408 txt = "Folgendes ist auf '"+ch+"' passiert:\n"
409 + implode(map(hist[<amount..], #'ChannelMessage/*'*/, 1), "");
MG Mud User88f12472016-06-24 23:31:02 +0200410 More(txt);
Arathorn85de7602019-11-28 23:04:03 +0100411 return 1;
412
413 default:
414 break;
Zesstra57a693e2019-01-06 22:08:24 +0100415 }
MG Mud User88f12472016-06-24 23:31:02 +0200416 }
417 }
Arathorn85de7602019-11-28 23:04:03 +0100418
419 if (sizeof(cmd = implode(cmd[2..], "")))
420 args = cmd + (sizeof(args) ? " " : "") + args;
MG Mud User88f12472016-06-24 23:31:02 +0200421
422 // KOntrollchars ausfiltern.
423 args = regreplace(args,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
Arathorn85de7602019-11-28 23:04:03 +0100424 if (!sizeof(args))
425 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200426
427 //Wenn cmd leer ist: MSG_SAY
Arathorn85de7602019-11-28 23:04:03 +0100428 if (!sizeof(cmd))
429 type = MSG_SAY;
430 else
431 {
432 switch (cmd[0])
MG Mud User88f12472016-06-24 23:31:02 +0200433 {
Arathorn85de7602019-11-28 23:04:03 +0100434 case ':' :
435 type = MSG_EMOTE;
436 args = args[1..];
437 break;
438 case ';' :
439 type = MSG_GEMOTE;
440 args = args[1..];
441 break;
442 case '\'':
443 args = args[1..];
444 // Der Fallthrough in default ist hier Absicht.
445 default :
446 type = MSG_SAY;
447 break;
MG Mud User88f12472016-06-24 23:31:02 +0200448 }
449 }
Arathorn85de7602019-11-28 23:04:03 +0100450
451 if (!ch || !sizeof(ch))
452 ch = QueryProp(P_STD_CHANNEL);
453
454 err = CHMASTER->send(ch, this_object(), args, type);
455 if (err < 0)
456 {
457 err = CHMASTER->join(ch, this_object());
458 if (!err)
MG Mud User88f12472016-06-24 23:31:02 +0200459 {
Arathorn85de7602019-11-28 23:04:03 +0100460 if (member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
MG Mud User88f12472016-06-24 23:31:02 +0200461 SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
462 err = CHMASTER->send(ch, this_object(), args, type);
463 }
Arathorn85de7602019-11-28 23:04:03 +0100464 }
MG Mud User88f12472016-06-24 23:31:02 +0200465
Arathorn85de7602019-11-28 23:04:03 +0100466 switch (err)
MG Mud User88f12472016-06-24 23:31:02 +0200467 {
Arathorn85de7602019-11-28 23:04:03 +0100468 case E_ACCESS_DENIED:
469 notify_fail("Auf der Ebene '"+ch+"' darfst Du nichts sagen.\n");
470 return 0;
471 case E_NOT_MEMBER:
472 notify_fail("Du hast die Ebene '"+ch+"' nicht betreten!\n");
473 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200474 }
475 return 1;
476}
477
478int ChannelAdmin(string args)
479{
Arathorn85de7602019-11-28 23:04:03 +0100480 args = _unparsed_args();
481
MG Mud User88f12472016-06-24 23:31:02 +0200482 string n, descr, sh, cn;
Arathorn00764692019-11-27 22:09:31 +0100483 mixed tmp;
MG Mud User88f12472016-06-24 23:31:02 +0200484 notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
485 " ebene <Abkuerzung>=\n"
486 " ebene abkuerzungen [standard]\n"
487 " ebene standard <Ebene>\n"
488 " ebene an|ein|aus\n"
489 +(QueryProp(P_LEVEL) >= 5 ?
490 " ebene neu <Name> <Bezeichnung>\n"
Arathorn85de7602019-11-28 23:04:03 +0100491 " ebene beschreibung <Name> <Beschreibung>\n" : "")
492 +(IS_ARCH(this_object()) ?
493 " ebene kill <Name>\n"
494 " ebene clear <Name>\n": ""));
495
496 if (!args || !sizeof(args))
497 return 0;
498
499 if (sscanf(args, "kill %s", n) && IS_ARCH(this_object()))
MG Mud User88f12472016-06-24 23:31:02 +0200500 {
Arathorn85de7602019-11-28 23:04:03 +0100501 cn = CHMASTER->find(n, this_object());
502 if (!cn)
503 cn = n;
504
505 if (CHMASTER->remove(cn, this_object()) == E_ACCESS_DENIED) {
506 notify_fail("Die Ebene '"+cn+"' liess sich nicht entfernen!\n");
507 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200508 }
Arathorn85de7602019-11-28 23:04:03 +0100509
MG Mud User88f12472016-06-24 23:31:02 +0200510 write("Du entfernst die Ebene '"+cn+"'.\n");
511 return 1;
512 }
Arathorn85de7602019-11-28 23:04:03 +0100513
514 if (sscanf(args, "clear %s", n) && IS_ARCH(this_object()))
MG Mud User88f12472016-06-24 23:31:02 +0200515 {
Arathorn85de7602019-11-28 23:04:03 +0100516 cn = CHMASTER->find(n, this_object());
517 if (!cn)
518 cn = n;
519
520 if (CHMASTER->clear_history(cn, this_object()) == E_ACCESS_DENIED) {
521 notify_fail("Der Verlauf zur Ebene '"+cn+"' liess sich nicht "
522 "entfernen!\n");
523 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200524 }
Arathorn85de7602019-11-28 23:04:03 +0100525
MG Mud User88f12472016-06-24 23:31:02 +0200526 write("Du entfernst den Verlauf zur Ebene '"+cn+"'.\n");
527 return 1;
528 }
Arathorn85de7602019-11-28 23:04:03 +0100529
530 if (sscanf(args, "neu %s %s", n, descr) == 2)
MG Mud User88f12472016-06-24 23:31:02 +0200531 {
Arathorn85de7602019-11-28 23:04:03 +0100532 if (QueryProp(P_LEVEL) < 5)
MG Mud User88f12472016-06-24 23:31:02 +0200533 {
Arathorn85de7602019-11-28 23:04:03 +0100534 notify_fail("Neue Ebenen zu erstellen, ist Dir verwehrt.\n");
535 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200536 }
Arathorn85de7602019-11-28 23:04:03 +0100537
538 if (!sizeof(regexp(({ n }), "^" CHANNELCMDS CHANNELCMDS "*")))
539 {
540 notify_fail("Der Name '"+n+"' ist nicht konform!\n");
541 return 0;
542 }
543
544 if (sizeof(n) > 20 )
545 {
546 notify_fail("Der Name '"+n+"' ist zu lang.\n");
547 return 0;
548 }
549
550 if (CHMASTER->new(n, this_object(), descr) == E_ACCESS_DENIED) {
551 notify_fail("Diese Ebene darfst du nicht erschaffen!\n");
552 return 0;
553 }
554 write("Du erschaffst die Ebene '"+n+"'.\n");
555 SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ lower_case(n) }));
556 return 1;
MG Mud User88f12472016-06-24 23:31:02 +0200557 }
Arathorn85de7602019-11-28 23:04:03 +0100558
559 if (sscanf(args, "beschreibung %s %s", n, descr) == 2)
MG Mud User88f12472016-06-24 23:31:02 +0200560 {
MG Mud User88f12472016-06-24 23:31:02 +0200561 cn = CHMASTER->find(n, this_object());
Arathorn85de7602019-11-28 23:04:03 +0100562 if (!cn || pointerp(cn))
563 {
564 notify_fail("Die Ebene '"+n+"' existiert nicht oder die Angabe "
565 "war nicht eindeutig.\n");
566 return 0;
567 }
568
569 mixed ch = CHMASTER->list(this_object());
570 if (ch[lower_case(cn)][I_MASTER] != this_object())
571 {
572 notify_fail("Du bist nicht berechtigt, die Beschreibung der Ebene"
573 " '"+cn+"' zu aendern.\n");
574 return 0;
575 }
MG Mud User88f12472016-06-24 23:31:02 +0200576 ch[lower_case(cn)][I_INFO] = descr;
577 write("Die Ebene '"+cn+"' hat ab sofort die Beschreibung:\n"+descr+"\n");
578 return 1;
579 }
Arathorn85de7602019-11-28 23:04:03 +0100580
581 if sscanf(args, "%s=%s", sh, n) == 2 && sizeof(n))
MG Mud User88f12472016-06-24 23:31:02 +0200582 {
Arathorn85de7602019-11-28 23:04:03 +0100583 tmp = CHMASTER->find(n, this_object());
584 if (pointerp(tmp) || !tmp)
585 {
586 notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"+
587 (pointerp(tmp) ?
588 implode(tmp, ", ") + "\n" :
589 "Ebene '"+n+"' nicht gefunden!\n"));
590 return 0;
591 }
592
593 mapping sc = QueryProp(P_CHANNEL_SHORT) || ([]);
594 m_add(sc, sh, tmp);
MG Mud User88f12472016-06-24 23:31:02 +0200595 SetProp(P_CHANNEL_SHORT, sc);
Arathorn85de7602019-11-28 23:04:03 +0100596
MG Mud User88f12472016-06-24 23:31:02 +0200597 shortcut = QueryProp(P_CHANNEL_SHORT);
Arathorn85de7602019-11-28 23:04:03 +0100598
MG Mud User88f12472016-06-24 23:31:02 +0200599 write("'"+sh+"' wird jetzt als Abkuerzung fuer '"+tmp+"' anerkannt.\n");
600 return 1;
601 }
Arathorn85de7602019-11-28 23:04:03 +0100602
603 if (sscanf(args, "%s=", sh))
MG Mud User88f12472016-06-24 23:31:02 +0200604 {
Arathorn85de7602019-11-28 23:04:03 +0100605 SetProp(P_CHANNEL_SHORT,
606 m_copy_delete(QueryProp(P_CHANNEL_SHORT) || ([]), sh));
MG Mud User88f12472016-06-24 23:31:02 +0200607 shortcut = QueryProp(P_CHANNEL_SHORT);
608 write("Du loeschst die Abkuerzung '"+sh+"'.\n");
609 return 1;
610 }
Arathorn85de7602019-11-28 23:04:03 +0100611
612 if (args == "an" || args == "ein")
MG Mud User88f12472016-06-24 23:31:02 +0200613 {
Arathorn85de7602019-11-28 23:04:03 +0100614 if (pointerp(QueryProp(P_SWAP_CHANNELS)))
MG Mud User88f12472016-06-24 23:31:02 +0200615 SetProp(P_CHANNELS, QueryProp(P_SWAP_CHANNELS));
616 else
617 SetProp(P_CHANNELS, m_indices(CHMASTER->list(this_object())));
Arathorn85de7602019-11-28 23:04:03 +0100618
619 // <excl> enthaelt die Channelnamen, deren Channel nicht erstellt wurden.
620 string* excl = RegisterChannels();
621 write("Du schaltest folgende Ebenen ein:\n"+
622 break_string(implode(QueryProp(P_CHANNELS) - excl, ", "), 78));
MG Mud User88f12472016-06-24 23:31:02 +0200623 SetProp(P_SWAP_CHANNELS, 0);
624 return 1;
625 }
Arathorn85de7602019-11-28 23:04:03 +0100626
627 if (args == "aus")
MG Mud User88f12472016-06-24 23:31:02 +0200628 {
629 SetProp(P_SWAP_CHANNELS, QueryProp(P_CHANNELS));
630 RemoveChannels();
631 SetProp(P_CHANNELS, ({}));
632 write("Du stellst die Ebenen ab.\n");
633 return 1;
634 }
Arathorn85de7602019-11-28 23:04:03 +0100635
Arathorn00764692019-11-27 22:09:31 +0100636 string* pa = old_explode(args, " ");
Arathorn85de7602019-11-28 23:04:03 +0100637 if (!strstr("abkuerzungen", pa[0]))
MG Mud User88f12472016-06-24 23:31:02 +0200638 {
Arathorn85de7602019-11-28 23:04:03 +0100639 string txt = "";
640 if (sizeof(pa) > 1 && !strstr("standard", pa[1]))
MG Mud User88f12472016-06-24 23:31:02 +0200641 {
Arathorn85de7602019-11-28 23:04:03 +0100642 write("Die Standardabkuerzungen werden gesetzt.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200643 SetProp(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
644 + (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
645 }
Arathorn85de7602019-11-28 23:04:03 +0100646 foreach (string abk, string ch_name : QueryProp(P_CHANNEL_SHORT)) {
Arathorndc28afc2018-11-26 22:20:59 +0100647 txt += sprintf("%5.5s = %s\n", abk, ch_name);
648 }
MG Mud User88f12472016-06-24 23:31:02 +0200649 txt = sprintf("Folgende Abkuerzungen sind definiert:\n%-78#s\n",
Arathorn85de7602019-11-28 23:04:03 +0100650 implode(sort_array(old_explode(txt, "\n"), #'>/*'*/), "\n"));
MG Mud User88f12472016-06-24 23:31:02 +0200651 More(txt);
652 return 1;
653 }
Arathorn85de7602019-11-28 23:04:03 +0100654
655 if (!strstr("standard", pa[0]))
656 {
657 if (sizeof(pa) < 2)
658 {
659 notify_fail("Benutzung: ebene standard <Ebene>\n"
660 +(QueryProp(P_STD_CHANNEL) ?
661 "Momentan ist '"+QueryProp(P_STD_CHANNEL)+
662 "' eingestellt.\n" :
663 "Es ist keine Standardebene eingestellt.\n"));
664 return 0;
665 }
MG Mud User88f12472016-06-24 23:31:02 +0200666 else
Arathorn85de7602019-11-28 23:04:03 +0100667 {
668 tmp = CHMASTER->find(pa[1], this_object());
669 if (pointerp(tmp))
670 {
671 notify_fail("Das war keine eindeutige Angabe! "
672 "Folgende Ebenen passen:\n"+
673 break_string(implode(tmp, ", "), 78));
674 return 0;
675 }
MG Mud User88f12472016-06-24 23:31:02 +0200676 else
Arathorn85de7602019-11-28 23:04:03 +0100677 {
678 if (!tmp)
679 {
680 notify_fail("Ebene '"+pa[1]+"' nicht gefunden!\n");
681 return 0;
682 }
MG Mud User88f12472016-06-24 23:31:02 +0200683 else
684 {
685 write("'"+tmp+"' ist jetzt die Standardebene.\n");
686 SetProp(P_STD_CHANNEL, tmp);
687 return 1;
688 }
Arathorn85de7602019-11-28 23:04:03 +0100689 }
690 }
691 }
MG Mud User88f12472016-06-24 23:31:02 +0200692 return(0);
693}
694