blob: d66ee26484a56a1b60b3a8150f81c5d858bb5190 [file] [log] [blame]
// MorgenGrauen MUDlib
//
// npc/info.c -- Behandeln von Fragen an den NPC
//
// $Id: info.c 9522 2016-03-01 19:20:10Z Arathorn $
/* Letzte Aenderungen von Wim 8.1.99
*
* AddInfo( schluessel, antwort [, indent [, [silent [, casebased] ] ] )
* Wenn ein Spieler dieses Monster nach "schluessel" fragt, so gib die
* Programmierte Antwort aus.
* Erweiterung von Wim: ist silent gesetzt, so erfolgt eine "persoenliche"
* Antwort. d.h. umstehende Personen bekommen bei silent==1 keinen, bei
* stringp(silent), den String ausgegeben, dabei kann er auch die Schluessel-
* Worte @WER @WESSEN @WEM @WEN enthalten.
* - Ergaenzt um @CAP_WER... fuer zwangs-capitalisierte Namen an Satzanfaengen.
* ist bei fragenden NPCs und PCs mit Tarnkappe wichtig! (Silvana)
* - Auch in der Antwort des fragenden wird nun ersetzt (Vanion)
* Enthaelt casedbased einen Funktionsnamen oder verweist auf eine closure, so
* wird die Beantwortung der Frage von dem return-Wert abhaengig gemacht.
* Bei 0 wird die Frage normal beantwortet, bei 1 erfolgt die Ausgabe des
* unter DEFAULT_NOINFO gespeicherten Textes.
* Wird ein String zurueckgegeben, so wird er unter Beachtung von ident an
* Stelle der urspruenglichen Information ausgegeben.
*
* RemoveInfo( schluessel )
* Das Monster antwortet nicht mehr auf diesen Schluessel.
*
* SetProp( P_DEFAULT_INFO, antwort [, indent ] )
* Setze die Antwort, die das Monster auf unverstaendliche Fragen geben
* soll. (Diese Funktion ist obsolet! Benutze stattdessen
* AddInfo( "\ndefault info", antwort [, indent ] );
*
* GetInfo( [schluessel] )
* Wenn Schluessel gesetzt ist, so wird die dazugehoerige Info,
* ansonsten werden alle Infos zurueckgegeben.
*
* Die Antworten sollten wie emote - kommandos aussehen.
* Der optionale Indent wird zum Umbrechen von langen Infos benutzt.
* (Typischerweise sollte indent="sagt: " sein.)
*
* In den Infos darf mit process_string gearbeitet werden. Das Ergebnis von
* process_string wird dann mit umgebrochen!
*
*---------------------------------------------------------------------------
*/
#pragma strong_types
#pragma save_types
#pragma range_check
#pragma no_clone
#pragma pedantic
#define NEED_PROTOTYPES
#include <thing/description.h>
#include <thing/properties.h>
#include <npc.h>
#undef NEED_PROTOTYPES
#include <properties.h>
#include <language.h>
#include <defines.h>
#include <config.h>
#include <exploration.h>
// TODO: langfristig waer hier private schoen.
nosave mapping infos;
protected void create()
{
// Initialisierung nur wenn noetig, damit beim virtuellen Erben von
// /std/npc in npc1 und npc2 dann in npc3 beim npc1::create();
// npc2:create(); im zweiten create() die Infos nicht
// ueberschrieben/geloescht werden.
if (!mappingp(infos)) {
infos = ([
DEFAULT_INFO:"schaut Dich fragend an.\n";0;
"schaut @WEN fragend an.\n";0,
DEFAULT_NOINFO:"moechte Dir nicht antworten.\n";0;
"verweigert @WEM die Antwort.\n";1
]);
}
}
void init() {
add_action( "frage", "frag", 1 );
}
static void smart_npc_log(string str)
{
string creat, creat_det;
if (!stringp(creat=QueryProp(P_LOG_INFO))) {
creat = MASTER->creator_file(this_object());
if (creat == ROOTID)
creat = "ROOT";
else if( creat==BACKBONEID )
creat="STD";
creat_det="report/"+explode(creat, ".")[<1]+"_INFO.rep";
creat="report/"+explode(creat, ".")[<1]+".rep";
}
log_file(creat,
sprintf("INFO von %s [%s] (%s):\n%s\n",
getuid(this_interactive()),
explode(object_name(this_object()),"#")[0],
strftime("%d. %b %Y"),
str));
if (stringp(creat_det) && sizeof(creat_det))
log_file(creat_det,
sprintf("INFO von %s [%s] (%s):\n%s\n",
getuid(this_interactive()),
explode(object_name(this_object()),"#")[0],
strftime("%d. %b %Y"),
str));
}
public int frage(string str) {
string myname, text;
str=(extern_call()?this_player()->_unparsed_args():str);
if( !str || sscanf( str, "%s nach %s", myname, text ) != 2 ) {
_notify_fail( "WEN willst Du nach WAS fragen?\n" );
return 0;
}
if( !id( lower_case(myname) )
|| QueryProp(P_INVIS) ) {
_notify_fail( "So jemanden findest Du hier nicht.\n" );
return 0;
}
say( capitalize(this_player()->name(WER))+" fragt " +
name(WEN,2)+" nach "+capitalize(text)+".\n",
this_player() );
text = lower_case(text);
GiveEP(EP_INFO, text);
return do_frage( text );
}
static string infoDefaultReplace(string pstring, object pl)
{
pstring=" "+pstring;
if (strstr(pstring,"@WER",0) >-1 )
pstring= regreplace(pstring,"@WER",pl->name(WER,1),1);
if (strstr(pstring,"@WESSEN",0) >-1 )
pstring= regreplace(pstring,"@WESSEN",pl->name(WESSEN,1),1);
if (strstr(pstring,"@WEM",0) >-1 )
pstring= regreplace(pstring,"@WEM",pl->name(WEM,1),1);
if (strstr(pstring,"@WEN",0) >-1 )
pstring= regreplace(pstring,"@WEN",pl->name(WEN,1),1);
if (strstr(pstring,"@CAP_WER",0) >-1 )
pstring= regreplace(pstring,"@CAP_WER",pl->Name(WER,1),1);
if (strstr(pstring,"@CAP_WESSEN",0) >-1 )
pstring= regreplace(pstring,"@CAP_WESSEN",pl->Name(WESSEN,1),1);
if (strstr(pstring,"@CAP_WEM",0) >-1 )
pstring= regreplace(pstring,"@CAP_WEM",pl->Name(WEM,1),1);
if (strstr(pstring,"@CAP_WEN",0) >-1 )
pstring= regreplace(pstring,"@CAP_WEN",pl->Name(WEN,1),1);
return pstring[1..];
}
static mixed *GetInfoArr(string str)
{
return ({ infos[str, 0], infos[str, 1], infos[str,2], infos[str, 3] });
}
public int do_frage(string text)
{
string indent,answer;
mixed silent, preinfo, noanswer;
mixed *info;
if (stringp(preinfo = QueryProp(P_PRE_INFO)))
{
// Die message action wird auf "frage" fixiert, damit dies immer das
// gleiche ist, egal, mit welchem Verb man in diese Funktion kommt.
this_player()->ReceiveMsg(preinfo, MT_LISTEN, "frage",
Name(WER,2)+" ",this_object());
send_room(environment(this_object()),
"ist nicht gewillt, "+this_player()->Name(WEM,2)
+" zu antworten.",
MT_LISTEN, "frage",
Name(WER,2)+" ",
({this_player()}) );
return 1;
}
else
{
if (intp(preinfo) && preinfo > 0)
return 1;
}
info=GetInfoArr(text);
if (!info[0])
{
if( this_interactive() && QueryProp(P_LOG_INFO) )
smart_npc_log(text);
text = DEFAULT_INFO;
info=GetInfoArr(text);
}
if (closurep(info[0]) ) {
answer=funcall(info[0]);
if( !answer || answer=="") return 1;
} else {
answer=process_string(info[0]);
}
if (closurep(info[3]) )
{
noanswer=funcall(info[3]);
if ( intp(noanswer) && noanswer > 0)
{
text = DEFAULT_NOINFO;
info = GetInfoArr(text);
if (closurep(info[0]) ) {
answer=funcall(info[0]);
if( !answer || answer=="") return 1;
} else {
answer=process_string(info[0]);
}
}
else if ( stringp(noanswer) )
answer = noanswer;
}
silent=info[2];
// Replacements gehen auch in der Antwort des NPC. Das gibt den Antworten
// eine persoenliche Note, und so teuer is das auch nicht :)
answer = infoDefaultReplace(answer, this_player());
if( indent=info[1] )
{
if (stringp(silent) || (intp(silent) && silent > 0) )
{ // Persoenliche Antwort mit indent
this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
"frage",
Name(WER,2)+" "+indent,
this_object());
if (stringp(silent))
{
silent=infoDefaultReplace(silent, this_player());
send_room(environment(), silent, MT_LISTEN, "frage",
Name(WER,2)+" ", ({this_player()}));
}
}
else // "normale Antwort" mit Indent
{
send_room(environment(), answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
"frage",
Name(WER,2)+" "+indent, ({this_object()}));
}
}
else
{
if (stringp(silent) || (intp(silent) && silent > 0) )
{ // Persoenliche Antwort ohne indent
this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_DONT_WRAP,
"frage",
Name(WER,2)+" ",
this_object());
if (stringp(silent))
{
silent=infoDefaultReplace(silent, this_player());
send_room(environment(), silent, MT_LISTEN, "frage",
Name(WER,2)+" ", ({this_player()}) );
}
}
else // "normale Antwort" ohne Indent
send_room(environment(), answer, MT_LISTEN|MSG_DONT_WRAP,
"frage", Name(WER,2)+" ");
}
return 1;
}
/*
*---------------------------------------------------------------------------
* Setzen von Infos
*---------------------------------------------------------------------------
*/
public varargs void AddInfo(mixed key, mixed info, string indent,
mixed silent, mixed casebased ) {
if (stringp(casebased))
casebased=symbol_function(casebased,this_object());
if( pointerp( key ) ) {
int i;
for ( i=sizeof( key )-1; i>=0; i-- )
infos += ([ key[i]: info; indent; silent; casebased ]);
}
else
infos += ([ key: info; indent; silent; casebased ]);
}
public varargs void AddSpecialInfo(mixed keys, string functionname,
string indent, mixed silent, mixed casebased )
{
int i;
closure cl;
if(!(cl=symbol_function(functionname,this_object()))) return;
return AddInfo(keys,cl,indent,silent,casebased);
}
public void RemoveInfo( string key )
{
m_delete(infos,key);
}
static varargs void _set_default_info( mixed info )
{
if (pointerp(info))
apply(#'AddInfo/*'*/,DEFAULT_INFO,info);
else
AddInfo(DEFAULT_INFO,info);
}
public varargs mixed GetInfo(string str)
{
if (!str) return deep_copy(infos);
return infos[str];
}
static mapping _query_npc_info()
{
return deep_copy(infos);
}
static mapping _set_npc_info( mapping map_ldfied )
{
if ( !mappingp(map_ldfied) )
return 0;
return infos = map_ldfied;
}