blob: 6b868f0a717eda63fcb70fd3283f0f0313958477 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// npc/info.c -- Behandeln von Fragen an den NPC
4//
5// $Id: info.c 9522 2016-03-01 19:20:10Z Arathorn $
6
7/* Letzte Aenderungen von Wim 8.1.99
8 *
9 * AddInfo( schluessel, antwort [, indent [, [silent [, casebased] ] ] )
10 * Wenn ein Spieler dieses Monster nach "schluessel" fragt, so gib die
11 * Programmierte Antwort aus.
12 * Erweiterung von Wim: ist silent gesetzt, so erfolgt eine "persoenliche"
13 * Antwort. d.h. umstehende Personen bekommen bei silent==1 keinen, bei
14 * stringp(silent), den String ausgegeben, dabei kann er auch die Schluessel-
15 * Worte @WER @WESSEN @WEM @WEN enthalten.
16 * - Ergaenzt um @CAP_WER... fuer zwangs-capitalisierte Namen an Satzanfaengen.
17 * ist bei fragenden NPCs und PCs mit Tarnkappe wichtig! (Silvana)
18 * - Auch in der Antwort des fragenden wird nun ersetzt (Vanion)
19 * Enthaelt casedbased einen Funktionsnamen oder verweist auf eine closure, so
20 * wird die Beantwortung der Frage von dem return-Wert abhaengig gemacht.
21 * Bei 0 wird die Frage normal beantwortet, bei 1 erfolgt die Ausgabe des
22 * unter DEFAULT_NOINFO gespeicherten Textes.
23 * Wird ein String zurueckgegeben, so wird er unter Beachtung von ident an
24 * Stelle der urspruenglichen Information ausgegeben.
25 *
26 * RemoveInfo( schluessel )
27 * Das Monster antwortet nicht mehr auf diesen Schluessel.
28 *
29 * SetProp( P_DEFAULT_INFO, antwort [, indent ] )
30 * Setze die Antwort, die das Monster auf unverstaendliche Fragen geben
31 * soll. (Diese Funktion ist obsolet! Benutze stattdessen
32 * AddInfo( "\ndefault info", antwort [, indent ] );
33 *
34 * GetInfo( [schluessel] )
35 * Wenn Schluessel gesetzt ist, so wird die dazugehoerige Info,
36 * ansonsten werden alle Infos zurueckgegeben.
37 *
38 * Die Antworten sollten wie emote - kommandos aussehen.
39 * Der optionale Indent wird zum Umbrechen von langen Infos benutzt.
40 * (Typischerweise sollte indent="sagt: " sein.)
41 *
42 * In den Infos darf mit process_string gearbeitet werden. Das Ergebnis von
43 * process_string wird dann mit umgebrochen!
44 *
45 *---------------------------------------------------------------------------
46 */
47#pragma strong_types
48#pragma save_types
49#pragma range_check
50#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +020051
52#define NEED_PROTOTYPES
53#include <thing/description.h>
54#include <thing/properties.h>
55#include <npc.h>
56#undef NEED_PROTOTYPES
57
58#include <properties.h>
59#include <language.h>
60#include <defines.h>
61#include <config.h>
62#include <exploration.h>
63
Zesstra4bfc05b2021-08-11 00:25:56 +020064#include <functionlist.h>
65
MG Mud User88f12472016-06-24 23:31:02 +020066// TODO: langfristig waer hier private schoen.
67nosave mapping infos;
68
69protected void create()
70{
71 // Initialisierung nur wenn noetig, damit beim virtuellen Erben von
72 // /std/npc in npc1 und npc2 dann in npc3 beim npc1::create();
73 // npc2:create(); im zweiten create() die Infos nicht
74 // ueberschrieben/geloescht werden.
Zesstra081ba9c2016-08-29 22:45:59 +020075 if (!mappingp(infos))
76 {
77 infos = m_allocate(20,4);
78 AddInfo(DEFAULT_INFO, "schaut Dich fragend an.\n", 0,
79 "schaut @WEN1 fragend an.\n", 0);
80 AddInfo(DEFAULT_NOINFO, "moechte Dir nicht antworten.\n", 0,
Arathorn9c1a6672020-09-24 21:34:09 +020081 "verweigert @WEM1 die Antwort.\n",
82 function int () { return 1; });
MG Mud User88f12472016-06-24 23:31:02 +020083 }
84}
85
86
Zesstra5b71ebb2018-03-07 20:50:35 +010087public varargs void init(object origin)
88{
MG Mud User88f12472016-06-24 23:31:02 +020089 add_action( "frage", "frag", 1 );
90}
91
92
93static void smart_npc_log(string str)
94{
Arathorn5fd69912019-12-03 00:31:39 +010095 string creat_det;
96 string|int creat = QueryProp(P_LOG_INFO);
97 if (!stringp(creat)) {
MG Mud User88f12472016-06-24 23:31:02 +020098 creat = MASTER->creator_file(this_object());
99 if (creat == ROOTID)
100 creat = "ROOT";
101 else if( creat==BACKBONEID )
102 creat="STD";
103 creat_det="report/"+explode(creat, ".")[<1]+"_INFO.rep";
104 creat="report/"+explode(creat, ".")[<1]+".rep";
105 }
106 log_file(creat,
107 sprintf("INFO von %s [%s] (%s):\n%s\n",
108 getuid(this_interactive()),
109 explode(object_name(this_object()),"#")[0],
110 strftime("%d. %b %Y"),
111 str));
112 if (stringp(creat_det) && sizeof(creat_det))
113 log_file(creat_det,
114 sprintf("INFO von %s [%s] (%s):\n%s\n",
115 getuid(this_interactive()),
116 explode(object_name(this_object()),"#")[0],
117 strftime("%d. %b %Y"),
118 str));
119}
120
121public int frage(string str) {
122 string myname, text;
123
124 str=(extern_call()?this_player()->_unparsed_args():str);
125 if( !str || sscanf( str, "%s nach %s", myname, text ) != 2 ) {
126 _notify_fail( "WEN willst Du nach WAS fragen?\n" );
127 return 0;
128 }
129
130 if( !id( lower_case(myname) )
131 || QueryProp(P_INVIS) ) {
132 _notify_fail( "So jemanden findest Du hier nicht.\n" );
133 return 0;
134 }
Zesstra29d26fb2021-05-07 10:22:53 +0200135
136 if (this_player()->QueryProp(P_DEAF))
137 {
138 this_player()->ReceiveMsg(
139 "Warum jemanden etwas fragen, wenn Du die Antwort gar nicht hoeren "
140 "koenntest?",
141 MT_NOTIFICATION, "frage");
142 return 1;
143 }
144
MG Mud User88f12472016-06-24 23:31:02 +0200145 say( capitalize(this_player()->name(WER))+" fragt " +
146 name(WEN,2)+" nach "+capitalize(text)+".\n",
147 this_player() );
148
149 text = lower_case(text);
150 GiveEP(EP_INFO, text);
151
152 return do_frage( text );
153}
154
Zesstra113b9192016-07-25 11:15:27 +0200155// ersetzt die alten @... durch die von replace_personal.
156private string conv2replace_personal(string pstring)
MG Mud User88f12472016-06-24 23:31:02 +0200157{
158 pstring=" "+pstring;
159 if (strstr(pstring,"@WER",0) >-1 )
Arathorn6ae13dc2018-01-06 00:07:03 +0100160 pstring= regreplace(pstring, "@WER([^1-9QU])", "@WER1\\1", 1);
MG Mud User88f12472016-06-24 23:31:02 +0200161 if (strstr(pstring,"@WESSEN",0) >-1 )
Arathorn6ae13dc2018-01-06 00:07:03 +0100162 pstring= regreplace(pstring, "@WESSEN([^1-9QU])", "@WESSEN1\\1", 1);
MG Mud User88f12472016-06-24 23:31:02 +0200163 if (strstr(pstring,"@WEM",0) >-1 )
Arathorn6ae13dc2018-01-06 00:07:03 +0100164 pstring= regreplace(pstring, "@WEM([^1-9QU])", "@WEM1\\1", 1);
MG Mud User88f12472016-06-24 23:31:02 +0200165 if (strstr(pstring,"@WEN",0) >-1 )
Arathorn6ae13dc2018-01-06 00:07:03 +0100166 pstring= regreplace(pstring, "@WEN([^1-9QU])", "@WEN1\\1", 1);
MG Mud User88f12472016-06-24 23:31:02 +0200167 if (strstr(pstring,"@CAP_WER",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200168 pstring= regreplace(pstring,"@CAP_WER","@WER1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200169 if (strstr(pstring,"@CAP_WESSEN",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200170 pstring= regreplace(pstring,"@CAP_WESSEN","@WESSEN1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200171 if (strstr(pstring,"@CAP_WEM",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200172 pstring= regreplace(pstring,"@CAP_WEM","@WEM1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200173 if (strstr(pstring,"@CAP_WEN",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200174 pstring= regreplace(pstring,"@CAP_WEN","@WEN1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200175
176 return pstring[1..];
177}
178
179static mixed *GetInfoArr(string str)
180{
181 return ({ infos[str, 0], infos[str, 1], infos[str,2], infos[str, 3] });
182}
183
184public int do_frage(string text)
185{
186 string indent,answer;
187 mixed silent, preinfo, noanswer;
188 mixed *info;
189
190 if (stringp(preinfo = QueryProp(P_PRE_INFO)))
191 {
192 // Die message action wird auf "frage" fixiert, damit dies immer das
193 // gleiche ist, egal, mit welchem Verb man in diese Funktion kommt.
194 this_player()->ReceiveMsg(preinfo, MT_LISTEN, "frage",
195 Name(WER,2)+" ",this_object());
196 send_room(environment(this_object()),
197 "ist nicht gewillt, "+this_player()->Name(WEM,2)
198 +" zu antworten.",
199 MT_LISTEN, "frage",
200 Name(WER,2)+" ",
201 ({this_player()}) );
202 return 1;
203 }
204 else
205 {
206 if (intp(preinfo) && preinfo > 0)
207 return 1;
208 }
209
210 info=GetInfoArr(text);
211 if (!info[0])
212 {
213 if( this_interactive() && QueryProp(P_LOG_INFO) )
214 smart_npc_log(text);
215 text = DEFAULT_INFO;
216 info=GetInfoArr(text);
217 }
218
219 if (closurep(info[0]) ) {
Zesstrabbb16a72021-08-09 23:58:46 +0200220 answer=funcall(info[0], text);
MG Mud User88f12472016-06-24 23:31:02 +0200221 if( !answer || answer=="") return 1;
222 } else {
223 answer=process_string(info[0]);
224 }
225
226 if (closurep(info[3]) )
227 {
Zesstrabbb16a72021-08-09 23:58:46 +0200228 noanswer=funcall(info[3], text);
MG Mud User88f12472016-06-24 23:31:02 +0200229 if ( intp(noanswer) && noanswer > 0)
230 {
231 text = DEFAULT_NOINFO;
232 info = GetInfoArr(text);
233 if (closurep(info[0]) ) {
Zesstrabbb16a72021-08-09 23:58:46 +0200234 answer=funcall(info[0], text);
MG Mud User88f12472016-06-24 23:31:02 +0200235 if( !answer || answer=="") return 1;
236 } else {
237 answer=process_string(info[0]);
238 }
239 }
240 else if ( stringp(noanswer) )
241 answer = noanswer;
242 }
243
244 silent=info[2];
245
246 // Replacements gehen auch in der Antwort des NPC. Das gibt den Antworten
247 // eine persoenliche Note, und so teuer is das auch nicht :)
Zesstra87033a82016-08-29 22:51:22 +0200248 answer = replace_personal(answer, ({this_player()}), 1);
MG Mud User88f12472016-06-24 23:31:02 +0200249
250 if( indent=info[1] )
251 {
252 if (stringp(silent) || (intp(silent) && silent > 0) )
253 { // Persoenliche Antwort mit indent
254 this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
255 "frage",
256 Name(WER,2)+" "+indent,
257 this_object());
258 if (stringp(silent))
259 {
Zesstra87033a82016-08-29 22:51:22 +0200260 silent=replace_personal(silent, ({this_player()}), 1);
MG Mud User88f12472016-06-24 23:31:02 +0200261 send_room(environment(), silent, MT_LISTEN, "frage",
262 Name(WER,2)+" ", ({this_player()}));
263 }
264 }
265 else // "normale Antwort" mit Indent
266 {
267 send_room(environment(), answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
268 "frage",
269 Name(WER,2)+" "+indent, ({this_object()}));
270 }
271 }
272 else
273 {
274 if (stringp(silent) || (intp(silent) && silent > 0) )
275 { // Persoenliche Antwort ohne indent
276 this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_DONT_WRAP,
277 "frage",
278 Name(WER,2)+" ",
279 this_object());
280 if (stringp(silent))
281 {
Zesstra87033a82016-08-29 22:51:22 +0200282 silent=replace_personal(silent, ({this_player()}), 1);
MG Mud User88f12472016-06-24 23:31:02 +0200283 send_room(environment(), silent, MT_LISTEN, "frage",
284 Name(WER,2)+" ", ({this_player()}) );
285 }
286 }
287 else // "normale Antwort" ohne Indent
288 send_room(environment(), answer, MT_LISTEN|MSG_DONT_WRAP,
289 "frage", Name(WER,2)+" ");
290 }
291 return 1;
292}
293
294/*
295 *---------------------------------------------------------------------------
296 * Setzen von Infos
297 *---------------------------------------------------------------------------
298 */
299
Arathornadecd712020-09-24 21:31:51 +0200300public varargs void AddInfo(string|string* key, string|closure info,
301 string indent, int|string silent, string|closure casebased)
302{
MG Mud User88f12472016-06-24 23:31:02 +0200303
304 if (stringp(casebased))
305 casebased=symbol_function(casebased,this_object());
Zesstra113b9192016-07-25 11:15:27 +0200306 if (stringp(info))
307 info = conv2replace_personal(info);
308 if (stringp(silent))
309 silent = conv2replace_personal(silent);
MG Mud User88f12472016-06-24 23:31:02 +0200310
311 if( pointerp( key ) ) {
312 int i;
313 for ( i=sizeof( key )-1; i>=0; i-- )
314 infos += ([ key[i]: info; indent; silent; casebased ]);
315 }
316 else
317 infos += ([ key: info; indent; silent; casebased ]);
318}
319
Arathornadecd712020-09-24 21:31:51 +0200320public varargs void AddSpecialInfo(string|string* key, string functionname,
321 string indent, int|string silent, string|closure casebased)
MG Mud User88f12472016-06-24 23:31:02 +0200322{
Zesstra50559382021-08-11 00:43:36 +0200323 closure cl = symbol_function(functionname,this_object());
324 if (!cl)
325 {
326 raise_error(sprintf(
327 "%s existiert nicht oder ist private.\n", functionname));
328 }
329
330 // Funktion existiert, aber externe Aufrufer durfen keine Closures auf
331 // static oder protected lfuns erstellen.
Zesstra4bfc05b2021-08-11 00:25:56 +0200332 if (extern_call())
333 {
334 <string|int>* fexist = function_exists(functionname,
335 FEXISTS_ALL|NAME_HIDDEN);
Zesstra50559382021-08-11 00:43:36 +0200336 if (fexist[FEXISTS_FLAGS] & (TYPE_MOD_STATIC|TYPE_MOD_PROTECTED) )
Zesstra4bfc05b2021-08-11 00:25:56 +0200337 {
338 raise_error(sprintf(
Zesstra50559382021-08-11 00:43:36 +0200339 "%s ist static oder protected und darf nicht durch "
340 "externe Aufrufer eingetragen werden.\n", functionname));
Zesstra4bfc05b2021-08-11 00:25:56 +0200341 }
342 }
MG Mud User88f12472016-06-24 23:31:02 +0200343
Arathorn621847c2020-09-28 19:58:26 +0200344 return AddInfo(key, cl, indent, silent, casebased);
MG Mud User88f12472016-06-24 23:31:02 +0200345}
346
347
348public void RemoveInfo( string key )
349{
350 m_delete(infos,key);
351}
352
353static varargs void _set_default_info( mixed info )
354{
355 if (pointerp(info))
356 apply(#'AddInfo/*'*/,DEFAULT_INFO,info);
357 else
358 AddInfo(DEFAULT_INFO,info);
359}
360
361public varargs mixed GetInfo(string str)
362{
363 if (!str) return deep_copy(infos);
364 return infos[str];
365}
366
367
368static mapping _query_npc_info()
369{
370 return deep_copy(infos);
371}
372
373
374static mapping _set_npc_info( mapping map_ldfied )
375{
376 if ( !mappingp(map_ldfied) )
377 return 0;
378
379 return infos = map_ldfied;
380}
381