blob: 9f8512a26ec4977a830110df8dfadb086af882d1 [file] [log] [blame]
// MorgenGrauen MUDlib
//
// channel.c -- channel client
//
// $Id: channel.c 9404 2015-12-13 00:21:44Z Zesstra $
#pragma strong_types
#pragma save_types
#pragma range_check
#pragma no_clone
#pragma pedantic
#define NEED_PROTOTYPES
#include <util.h>
#include <thing/properties.h>
#include <living/comm.h>
#include <player.h>
#include <player/comm.h>
#include <daemon.h>
#include <player/gmcp.h>
#undef NEED_PROTOTYPES
#include <wizlevels.h>
#include <defines.h>
#include <properties.h>
#include <sys_debug.h>
#include <regexp.h>
#define P_SWAP_CHANNELS "swap_channels"
#define P_CHANNEL_SHORT "short_channels"
#define CHANNELCMDS "[#@%$&()<>a-zA-Z0-9\\-]"
#define DEFAULT_CHANNELS ({"Abenteuer", "Anfaenger","Grats","Tod", "ZT"})
#define DEFAULT_SHORTCUTS \
([ \
"b":"Abenteuer", \
"a":"Allgemein", \
"B":"Beileid", \
"q":"D-chat", \
"G":"Grats", \
"M":"Moerder", \
"h":"Seher", \
"T":"Tod", \
])
#define WIZARD_SHORTCUTS \
([ \
"P":"D-code", \
"D":"Debug", \
"O":"Intercode", \
"I":"Intermud", \
"m":"Magier", \
])
private nosave mapping shortcut;
private nosave int c_status;
void create()
{
Set(P_CHANNELS, SAVE, F_MODE);
Set(P_CHANNELS, DEFAULT_CHANNELS);
Set(P_SWAP_CHANNELS, SAVE, F_MODE);
Set(P_STD_CHANNEL, "Allgemein");
Set(P_STD_CHANNEL, SAVE, F_MODE);
Set(P_CHANNEL_SHORT, SAVE, F_MODE);
Set(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
+ (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
}
static mixed _query_localcmds()
{
return ({({"-","ChannelParser", 1, 0}),
({"ebene", "ChannelAdmin", 0, 0}),
({"ebenen", "ChannelAdmin", 1, 0}),
});
}
mixed RegisterChannels()
{
mixed err;
if(extern_call() &&
previous_object() != find_object(CHMASTER)) return;
c_status = 0;
shortcut = QueryProp(P_CHANNEL_SHORT);
SetProp(P_CHANNELS, map(QueryProp(P_CHANNELS) || ({}),
#'lower_case));
err = filter(QueryProp(P_CHANNELS),
symbol_function("join", CHMASTER),
this_object());
if(QueryProp(P_LEVEL) < 5) return err;
while(sizeof(err)) {
CHMASTER->new(err[0], this_object());
err[0..0] = ({});
}
return err;
}
mixed RemoveChannels()
{
closure cl;
mixed err=({});
if(extern_call() &&
previous_object() != find_object(CHMASTER)) return;
if(!c_status) c_status = 1;
else return ({});
cl=symbol_function("leave", CHMASTER);
if (closurep(cl)) {
err = filter(QueryProp(P_CHANNELS), cl, this_object());
SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - err);
}
return err;
}
varargs private string getName(mixed x, int fall) {
mixed o = closurep(x) ? query_closure_object(x) : x;
if(stringp(o) && sizeof(o) && (x = find_object(o)))
o = x;
// Objekte
if (objectp(o)) {
// Magier sehen unsichtbare nicht nur als "Jemand"
if (o->QueryProp(P_INVIS) && IS_LEARNING(this_object()))
return "("+capitalize(getuid(o))+")";
// Froesche mit Namen versorgen.
if (o->QueryProp(P_FROG))
return "Frosch "+capitalize(getuid(o));
// Default (Unsichtbare als "Jemand" (s. Name()))
return o->Name(fall, 2)||"<Unbekannt>";
}
// Strings
else if (stringp(o) && sizeof(o)) {
if (o[0] == '/') {
// unsichtbare Objekte...
int p = strstr(o, "$");
if (p != -1) {
// Magier im Magiermodus kriegen den Realnamen, andere nicht.
if (IS_LEARNING(this_object()))
return o[1..p-1];
else
return o[p+1..];
}
else
// doch nicht unsichtbar
return (fall == WESSEN ? o+"s" : o);
}
else
// nicht unsichtbar
return (fall == WESSEN ? o+"s" : o);
}
// Fall-through
return "<Unbekannt>";
}
// <nonint> unterdrueckt die AUsgabe an den Spieler und liefert den Text
// zurueck. Wird nur fuer die Ebenenhistory benutzt.
string ChannelMessage(mixed* msg, int nonint)
{
string channel_message;
string channel=msg[0];
object sender=msg[1];
string message=msg[2];
int msg_type = msg[3];
if ( previous_object() != find_object(CHMASTER) &&
previous_object() != ME )
return 0;
string sender_name = getName(sender, msg_type == MSG_GEMOTE ? WESSEN : WER);
int prepend_indent_flag=QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0;
switch(msg_type) {
case MSG_EMPTY:
channel_message= message+"\n";
break;
case MSG_GEMOTE:
case MSG_EMOTE:
channel_message = break_string(sender_name + " "+ message+"]",
78, sprintf("[%s:", channel),
BS_INDENT_ONCE|prepend_indent_flag);
break;
case MSG_SAY:
default:
string presay=sprintf("[%s:%s] ", channel, sender_name);
channel_message = break_string(message, max(78,sizeof(presay)+10),
presay, prepend_indent_flag);
break;
}
if(nonint)
return channel_message;
// Wenn GMCP sich um Uebertragung der Nachricht kuemmert, wird ReceiveMsg()
// nicht mehr aufgerufen.
if (GMCP_Channel(channel_message, channel, sender_name) != 1)
{
// Der Ebenenname muss in Kleinbuchstaben uebergeben werden, damit die
// Ignorierepruefung funktioniert. Die ignorierestrings sind naemlich alle
// kleingeschrieben.
ReceiveMsg(channel_message,
MT_COMM|MT_FAR|MSG_DONT_STORE|MSG_DONT_WRAP,
MA_CHANNEL"." + lower_case(channel), 0, sender);
}
return 0;
}
private void createList(string n, mixed a, mixed m, mixed l)
{
int pos; string sh, *mem;
if((pos = member(map(m_values(shortcut), #'lower_case/*'*/), n)) != -1)
sh = m_indices(shortcut)[pos];
else sh = "";
mem=map(a[I_MEMBER],#'getName/*'*/, WER);
mem-=({"<MasteR>"});
l += ({ sprintf("%-12.12'.'s %c[%-1.1s] %|12.12' 's (%-|3' 'd) %-42.42s\n",
a[I_NAME], (member(m, n) != -1 ? '*' : ' '), sh,
a[I_MASTER] ?
getName(a[I_MASTER]) : getName(a[I_ACCESS]),
sizeof(mem),
(closurep(a[I_INFO]) && objectp(query_closure_object(a[I_INFO]))) ?
funcall(a[I_INFO]) || "- Keine Beschreibung -" :
(stringp(a[I_INFO]) ? a[I_INFO] : "- Keine Beschreibung -")
) });
}
private mixed getChannel(string ch)
{
mixed ff;
if(!sizeof(ch)) ch = QueryProp(P_STD_CHANNEL);
if(shortcut && shortcut[ch]) ch = shortcut[ch];
return CHMASTER->find(ch, this_object());
}
#ifndef DEBUG
#define DEBUG(x) if (funcall(symbol_function('find_player),"zesstra"))\
tell_object(funcall(symbol_function('find_player),"zesstra"),\
"MDBG: "+x+"\n")
#endif
int ChannelParser(string args)
{
mixed ch, cmd, tmp;
int pos, type, err;
string txt;
cmd = query_verb();
args = _unparsed_args();
notify_fail("Benutzung: -<Ebene>[ ]['|:|;]<Text>\n"
" -<Ebene>[+|-|?|!|*]\n"
" -?\n");
if(!cmd && !args) return 0;
if(!args) args = "";
cmd = cmd[1..];
if(sizeof(cmd = regexplode(cmd,
"^" CHANNELCMDS "*"
"([+-]|\\!|\\?|\\*)*")) > 1)
{
//z.B. cmd= ({"","allgemein",":testet"})
if(sizeof(cmd[1]) > 1 &&
strstr("+-?!*", cmd[1][<1..<1]) > -1)
tmp = cmd[1][0..<2];
else
tmp = cmd[1];
if(cmd[1] != "?" && cmd[1] != "!")
if(pointerp(ch = getChannel(tmp)))
{
notify_fail("Diese Angabe war nicht eindeutig! "
"Folgende Ebenen passen:\n"
+implode(ch, ", ")+"\n");
return 0;
}
else if(!ch) return (notify_fail("Die Ebene '"+tmp
+"' gibt es nicht!\n"), 0);
//DEBUG(sprintf("ChanCmd: %O\n",cmd));
if (sizeof(cmd[1])) {
switch(cmd[1][<1]) {
case '+':
switch(CHMASTER->join(ch, this_object()))
{
case E_ACCESS_DENIED:
notify_fail("Du darfst an die Ebene '"+ch+"' nicht heran.\n");
return 0;
case E_ALREADY_JOINED:
notify_fail("Du hast diese Ebene schon betreten!\n");
return 0;
default: break;
}
write("Du betrittst die Ebene '"+ch+"'.\n");
if(member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
return 1;
case '-':
switch(CHMASTER->leave(ch, this_object()))
{
case E_ACCESS_DENIED:
write("Du kannst die Ebene '"+ch+"' nicht verlassen.\n");
break;
case E_NOT_MEMBER:
write("Wie willst Du eine Ebene verlassen, welche Du nicht "
"betreten hast?\n");
break;
default:
write("Du verlaesst die Ebene '"+ch+"'.\n");
SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - ({ lower_case(ch), ch }));
break;
}
return 1;
case '!':
case '?':
{
mapping l;
if(mappingp(l = CHMASTER->list(this_object())))
if(stringp(ch) && sizeof(ch) && pointerp(l[ch = lower_case(ch)]))
{
int c; object o; string n; string *m;
m=sort_array(map(l[ch][I_MEMBER],#'getName/*'*/, WER),#'>/*'*/);
m-=({"<MasteR>"});
write(l[ch][I_NAME]+", "+funcall(l[ch][I_INFO])+".\n");
write("Du siehst "+((c = sizeof(m)) > 0
? (c == 1 ? "ein Gesicht" : c+" Gesichter")
: "niemanden")+" auf der Ebene '"
+l[ch][I_NAME]+"':\n");
write(break_string(implode(m,", "), 78));
write((l[ch][I_MASTER] ?
getName(l[ch][I_MASTER]) : getName(l[ch][I_ACCESS], WER))
+" hat das Sagen auf dieser Ebene.\n");
}
else
{
mixed list; list = ({});
if(cmd[1][<1] == '!')
l -= mkmapping(m_indices(l) - QueryProp(P_CHANNELS));
walk_mapping(l, #'createList/*'*/, QueryProp(P_CHANNELS), &list);
list = sort_array(list, #'>/*'*/);
txt = sprintf("%-12.12' 's [A] %|12' 's (%-3' 's) %-42.42s\n",
"Name", "Eigner", "Sp", "Beschreibung")
+ "-------------------------------------------------------"
+ "-----------------------\n"
+ implode(list, "");
More(txt);
}
return 1;
}
case '*':
{
mixed hist; int amount;
if(!pointerp(hist = CHMASTER->history(ch, this_object()))
|| !sizeof(hist))
{
write("Es ist keine Geschichte fuer '"+ch+"' verfuegbar.\n");
return 1;
}
//(Zesstra) cmd hat offenbar immer 3 Elemente...
//bei -all* ({"","all*",""})
//bei -all*10 ({"","all*,"10"})
//also ist bei -all* amount immer == 0 und es funktioniert eher zufaellig.
/*if(sizeof(cmd) > 2)
amount = to_int(cmd[2]);
else
amount=sizeof(hist);*/
amount=to_int(cmd[2]);
if (amount <= 0 || amount >= sizeof(hist))
amount=sizeof(hist);
txt = "Folgendes ist auf '"+ch+"' passiert:\n"
+ implode(map(hist[<amount..], #'ChannelMessage/*'*/, 1), "");
More(txt);
return 1;
}
default:
break;
}
}
}
if(sizeof(cmd = implode(cmd[2..], "")))
args = cmd + (sizeof(args) ? " " : "") + args;
// KOntrollchars ausfiltern.
args = regreplace(args,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
if(!sizeof(args)) return 0;
//Wenn cmd leer ist: MSG_SAY
if (!sizeof(cmd)) type=MSG_SAY;
else {
switch(cmd[0])
{
case ':' :
type = MSG_EMOTE;
args = args[1..];
break;
case ';' :
type = MSG_GEMOTE;
args = args[1..];
break;
case '\'':
args = args[1..];
default : type = MSG_SAY; break;
}
}
if(!ch || !sizeof(ch)) ch = QueryProp(P_STD_CHANNEL);
if((err = CHMASTER->send(ch, this_object(), args, type)) < 0)
if(!(err = CHMASTER->join(ch, this_object())))
{
if(member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
err = CHMASTER->send(ch, this_object(), args, type);
}
switch(err)
{
case E_ACCESS_DENIED:
notify_fail("Auf der Ebene '"+ch+"' darfst Du nichts sagen.\n");
return 0;
case E_NOT_MEMBER:
notify_fail("Du hast die Ebene '"+ch+"' nicht betreten!\n");
return 0;
}
return 1;
}
int ChannelAdmin(string args)
{
string n, descr, sh, cn;
mixed pa, tmp;
args = _unparsed_args();
notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
" ebene <Abkuerzung>=\n"
" ebene abkuerzungen [standard]\n"
" ebene standard <Ebene>\n"
" ebene an|ein|aus\n"
+(QueryProp(P_LEVEL) >= 5 ?
" ebene neu <Name> <Bezeichnung>\n"
" ebene beschreibung <Name> <Beschreibung>\n" : "")
+(IS_ARCH(this_object()) ?
" ebene kill <Name>\n"
" ebene clear <Name>\n": ""));
if(!args || !sizeof(args)) return 0;
if(sscanf(args, "kill %s", n) && IS_ARCH(this_object()))
{
if(!(cn = CHMASTER->find(n, this_object()))) cn = n;
switch(CHMASTER->remove(cn, this_object()))
{
case E_ACCESS_DENIED:
notify_fail("Die Ebene '"+cn+"' lies sich nicht entfernen!\n");
return 0;
}
write("Du entfernst die Ebene '"+cn+"'.\n");
return 1;
}
if(sscanf(args, "clear %s", n) && IS_ARCH(this_object()))
{
if(!(cn = CHMASTER->find(n, this_object()))) cn = n;
switch(CHMASTER->clear_history(cn, this_object()))
{
case E_ACCESS_DENIED:
notify_fail("Der Verlauf zur Ebene '"+cn+"' lies sich nicht entfernen!\n");
return 0;
}
write("Du entfernst den Verlauf zur Ebene '"+cn+"'.\n");
return 1;
}
if(sscanf(args, "neu %s %s", n, descr) == 2)
{
mixed x;
if(QueryProp(P_LEVEL) < 5)
return (notify_fail("Neue Ebenen zu erstellen ist dir verwehrt.\n"), 0);
if(!sizeof(regexp(({ n }), "^" CHANNELCMDS CHANNELCMDS "*")))
return (notify_fail("Der Name '"+n+"' ist nicht konform!\n"), 0);
if (sizeof(n) > 20 )
return(notify_fail("Der Name '"+n+"' ist zu lang.\n"), 0);
switch(x = CHMASTER->new(n, this_object(), descr))
{
case E_ACCESS_DENIED:
notify_fail("Diese Ebene darfst du nicht erschaffen!\n"); break;
default:
write("Du erschaffst die Ebene '"+n+"'.\n");
SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ lower_case(n) }));
return 1;
}
}
if(sscanf(args, "beschreibung %s %s", n, descr) == 2)
{
mixed ch;
cn = CHMASTER->find(n, this_object());
if(!cn || pointerp(cn))
return (notify_fail("Die Ebene '"+n+"' existiert nicht oder die Angabe "
"war nicht eindeutig.\n"), 0);
ch = CHMASTER->list(this_object());
if(ch[lower_case(cn)][I_MASTER] != this_object())
return (notify_fail("Du bist nicht berechtigt die Beschreibung der Ebene"
" '"+cn+"' zu aendern.\n"), 0);
ch[lower_case(cn)][I_INFO] = descr;
write("Die Ebene '"+cn+"' hat ab sofort die Beschreibung:\n"+descr+"\n");
return 1;
}
if(sscanf(args, "%s=%s", sh, n) == 2 && sizeof(n))
{
mapping sc;
if(pointerp(tmp = CHMASTER->find(n, this_object())) || !tmp)
return (notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
+(pointerp(tmp) ? implode(tmp, ", ") + "\n" :
"Ebene '"+n+"' nicht gefunden!\n")), 0);
sc = QueryProp(P_CHANNEL_SHORT);
if(!sc) sc = ([]);
sc[sh] = tmp;
SetProp(P_CHANNEL_SHORT, sc);
shortcut = QueryProp(P_CHANNEL_SHORT);
write("'"+sh+"' wird jetzt als Abkuerzung fuer '"+tmp+"' anerkannt.\n");
return 1;
}
if(sscanf(args, "%s=", sh))
{
SetProp(P_CHANNEL_SHORT, m_copy_delete(QueryProp(P_CHANNEL_SHORT) || ([]), sh));
shortcut = QueryProp(P_CHANNEL_SHORT);
write("Du loeschst die Abkuerzung '"+sh+"'.\n");
return 1;
}
if(args == "an" || args == "ein")
{
mixed excl;
if(pointerp(QueryProp(P_SWAP_CHANNELS)))
SetProp(P_CHANNELS, QueryProp(P_SWAP_CHANNELS));
else
SetProp(P_CHANNELS, m_indices(CHMASTER->list(this_object())));
excl = RegisterChannels();
write("Du schaltest folgende Ebenen ein:\n"
+break_string(implode(QueryProp(P_CHANNELS) - excl, ", "), 78));
SetProp(P_SWAP_CHANNELS, 0);
return 1;
}
if(args == "aus")
{
SetProp(P_SWAP_CHANNELS, QueryProp(P_CHANNELS));
RemoveChannels();
SetProp(P_CHANNELS, ({}));
write("Du stellst die Ebenen ab.\n");
return 1;
}
pa = old_explode(args, " ");
if(!strstr("abkuerzungen", pa[0]))
{
string txt; txt = "";
if(sizeof(pa) > 1 && !strstr("standard", pa[1]))
{
write("Die Standard Abkuerzungen werden gesetzt.\n");
SetProp(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
+ (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
}
walk_mapping(QueryProp(P_CHANNEL_SHORT),
lambda(({'i/*'*/, 'c, 'r}),
({#'+=, 'r/*'*/,
({#'sprintf/*'*/, "%5.5s = %s\n", 'i, 'c})})),
&txt);
txt = sprintf("Folgende Abkuerzungen sind definiert:\n%-78#s\n",
implode(sort_array(old_explode(txt, "\n"), #'>/*'*/), "\n"));
More(txt);
return 1;
}
if(!strstr("standard", pa[0]))
if(sizeof(pa) < 2)
return (notify_fail("Benutzung: ebene standard <Ebene>\n"
+(QueryProp(P_STD_CHANNEL)
? "Momentan ist '"+QueryProp(P_STD_CHANNEL)
+"' eingestellt.\n"
: "Es ist keine Standardebene eingestellt.\n")),0);
else
if(pointerp(tmp = CHMASTER->find(pa[1], this_object())))
return (notify_fail("Das war keine eindeutige Angabe! "
"Folgende Ebenen passen:\n"
+break_string(implode(tmp, ", "), 78)), 0);
else
if(!tmp) return (notify_fail("Ebene '"+pa[1]+"' nicht gefunden!\n"),0);
else
{
write("'"+tmp+"' ist jetzt die Standardebene.\n");
SetProp(P_STD_CHANNEL, tmp);
return 1;
}
return(0);
}