blob: 02dae3e26d08baf2a27a37b6d21004d412cc7c61 [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
51#pragma pedantic
52
53#define NEED_PROTOTYPES
54#include <thing/description.h>
55#include <thing/properties.h>
56#include <npc.h>
57#undef NEED_PROTOTYPES
58
59#include <properties.h>
60#include <language.h>
61#include <defines.h>
62#include <config.h>
63#include <exploration.h>
64
65// TODO: langfristig waer hier private schoen.
66nosave mapping infos;
67
68protected void create()
69{
70 // Initialisierung nur wenn noetig, damit beim virtuellen Erben von
71 // /std/npc in npc1 und npc2 dann in npc3 beim npc1::create();
72 // npc2:create(); im zweiten create() die Infos nicht
73 // ueberschrieben/geloescht werden.
Zesstra081ba9c2016-08-29 22:45:59 +020074 if (!mappingp(infos))
75 {
76 infos = m_allocate(20,4);
77 AddInfo(DEFAULT_INFO, "schaut Dich fragend an.\n", 0,
78 "schaut @WEN1 fragend an.\n", 0);
79 AddInfo(DEFAULT_NOINFO, "moechte Dir nicht antworten.\n", 0,
80 "verweigert @WEM1 die Antwort.\n", 1);
MG Mud User88f12472016-06-24 23:31:02 +020081 }
82}
83
84
85void init() {
86 add_action( "frage", "frag", 1 );
87}
88
89
90static void smart_npc_log(string str)
91{
92 string creat, creat_det;
93
94 if (!stringp(creat=QueryProp(P_LOG_INFO))) {
95 creat = MASTER->creator_file(this_object());
96 if (creat == ROOTID)
97 creat = "ROOT";
98 else if( creat==BACKBONEID )
99 creat="STD";
100 creat_det="report/"+explode(creat, ".")[<1]+"_INFO.rep";
101 creat="report/"+explode(creat, ".")[<1]+".rep";
102 }
103 log_file(creat,
104 sprintf("INFO von %s [%s] (%s):\n%s\n",
105 getuid(this_interactive()),
106 explode(object_name(this_object()),"#")[0],
107 strftime("%d. %b %Y"),
108 str));
109 if (stringp(creat_det) && sizeof(creat_det))
110 log_file(creat_det,
111 sprintf("INFO von %s [%s] (%s):\n%s\n",
112 getuid(this_interactive()),
113 explode(object_name(this_object()),"#")[0],
114 strftime("%d. %b %Y"),
115 str));
116}
117
118public int frage(string str) {
119 string myname, text;
120
121 str=(extern_call()?this_player()->_unparsed_args():str);
122 if( !str || sscanf( str, "%s nach %s", myname, text ) != 2 ) {
123 _notify_fail( "WEN willst Du nach WAS fragen?\n" );
124 return 0;
125 }
126
127 if( !id( lower_case(myname) )
128 || QueryProp(P_INVIS) ) {
129 _notify_fail( "So jemanden findest Du hier nicht.\n" );
130 return 0;
131 }
132 say( capitalize(this_player()->name(WER))+" fragt " +
133 name(WEN,2)+" nach "+capitalize(text)+".\n",
134 this_player() );
135
136 text = lower_case(text);
137 GiveEP(EP_INFO, text);
138
139 return do_frage( text );
140}
141
Zesstra113b9192016-07-25 11:15:27 +0200142// ersetzt die alten @... durch die von replace_personal.
143private string conv2replace_personal(string pstring)
MG Mud User88f12472016-06-24 23:31:02 +0200144{
145 pstring=" "+pstring;
146 if (strstr(pstring,"@WER",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200147 pstring= regreplace(pstring,"@WER","@WER1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200148 if (strstr(pstring,"@WESSEN",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200149 pstring= regreplace(pstring,"@WESSEN","@WESSEN1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200150 if (strstr(pstring,"@WEM",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200151 pstring= regreplace(pstring,"@WEM","@WEM1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200152 if (strstr(pstring,"@WEN",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200153 pstring= regreplace(pstring,"@WEN","@WEN1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200154 if (strstr(pstring,"@CAP_WER",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200155 pstring= regreplace(pstring,"@CAP_WER","@WER1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200156 if (strstr(pstring,"@CAP_WESSEN",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200157 pstring= regreplace(pstring,"@CAP_WESSEN","@WESSEN1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200158 if (strstr(pstring,"@CAP_WEM",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200159 pstring= regreplace(pstring,"@CAP_WEM","@WEM1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200160 if (strstr(pstring,"@CAP_WEN",0) >-1 )
Zesstra113b9192016-07-25 11:15:27 +0200161 pstring= regreplace(pstring,"@CAP_WEN","@WEN1",1);
MG Mud User88f12472016-06-24 23:31:02 +0200162
163 return pstring[1..];
164}
165
166static mixed *GetInfoArr(string str)
167{
168 return ({ infos[str, 0], infos[str, 1], infos[str,2], infos[str, 3] });
169}
170
171public int do_frage(string text)
172{
173 string indent,answer;
174 mixed silent, preinfo, noanswer;
175 mixed *info;
176
177 if (stringp(preinfo = QueryProp(P_PRE_INFO)))
178 {
179 // Die message action wird auf "frage" fixiert, damit dies immer das
180 // gleiche ist, egal, mit welchem Verb man in diese Funktion kommt.
181 this_player()->ReceiveMsg(preinfo, MT_LISTEN, "frage",
182 Name(WER,2)+" ",this_object());
183 send_room(environment(this_object()),
184 "ist nicht gewillt, "+this_player()->Name(WEM,2)
185 +" zu antworten.",
186 MT_LISTEN, "frage",
187 Name(WER,2)+" ",
188 ({this_player()}) );
189 return 1;
190 }
191 else
192 {
193 if (intp(preinfo) && preinfo > 0)
194 return 1;
195 }
196
197 info=GetInfoArr(text);
198 if (!info[0])
199 {
200 if( this_interactive() && QueryProp(P_LOG_INFO) )
201 smart_npc_log(text);
202 text = DEFAULT_INFO;
203 info=GetInfoArr(text);
204 }
205
206 if (closurep(info[0]) ) {
207 answer=funcall(info[0]);
208 if( !answer || answer=="") return 1;
209 } else {
210 answer=process_string(info[0]);
211 }
212
213 if (closurep(info[3]) )
214 {
215 noanswer=funcall(info[3]);
216 if ( intp(noanswer) && noanswer > 0)
217 {
218 text = DEFAULT_NOINFO;
219 info = GetInfoArr(text);
220 if (closurep(info[0]) ) {
221 answer=funcall(info[0]);
222 if( !answer || answer=="") return 1;
223 } else {
224 answer=process_string(info[0]);
225 }
226 }
227 else if ( stringp(noanswer) )
228 answer = noanswer;
229 }
230
231 silent=info[2];
232
233 // Replacements gehen auch in der Antwort des NPC. Das gibt den Antworten
234 // eine persoenliche Note, und so teuer is das auch nicht :)
Zesstra87033a82016-08-29 22:51:22 +0200235 answer = replace_personal(answer, ({this_player()}), 1);
MG Mud User88f12472016-06-24 23:31:02 +0200236
237 if( indent=info[1] )
238 {
239 if (stringp(silent) || (intp(silent) && silent > 0) )
240 { // Persoenliche Antwort mit indent
241 this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
242 "frage",
243 Name(WER,2)+" "+indent,
244 this_object());
245 if (stringp(silent))
246 {
Zesstra87033a82016-08-29 22:51:22 +0200247 silent=replace_personal(silent, ({this_player()}), 1);
MG Mud User88f12472016-06-24 23:31:02 +0200248 send_room(environment(), silent, MT_LISTEN, "frage",
249 Name(WER,2)+" ", ({this_player()}));
250 }
251 }
252 else // "normale Antwort" mit Indent
253 {
254 send_room(environment(), answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
255 "frage",
256 Name(WER,2)+" "+indent, ({this_object()}));
257 }
258 }
259 else
260 {
261 if (stringp(silent) || (intp(silent) && silent > 0) )
262 { // Persoenliche Antwort ohne indent
263 this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_DONT_WRAP,
264 "frage",
265 Name(WER,2)+" ",
266 this_object());
267 if (stringp(silent))
268 {
Zesstra87033a82016-08-29 22:51:22 +0200269 silent=replace_personal(silent, ({this_player()}), 1);
MG Mud User88f12472016-06-24 23:31:02 +0200270 send_room(environment(), silent, MT_LISTEN, "frage",
271 Name(WER,2)+" ", ({this_player()}) );
272 }
273 }
274 else // "normale Antwort" ohne Indent
275 send_room(environment(), answer, MT_LISTEN|MSG_DONT_WRAP,
276 "frage", Name(WER,2)+" ");
277 }
278 return 1;
279}
280
281/*
282 *---------------------------------------------------------------------------
283 * Setzen von Infos
284 *---------------------------------------------------------------------------
285 */
286
Zesstra113b9192016-07-25 11:15:27 +0200287public varargs void AddInfo(mixed key, mixed info, string indent,
MG Mud User88f12472016-06-24 23:31:02 +0200288 mixed silent, mixed casebased ) {
289
290 if (stringp(casebased))
291 casebased=symbol_function(casebased,this_object());
Zesstra113b9192016-07-25 11:15:27 +0200292 if (stringp(info))
293 info = conv2replace_personal(info);
294 if (stringp(silent))
295 silent = conv2replace_personal(silent);
MG Mud User88f12472016-06-24 23:31:02 +0200296
297 if( pointerp( key ) ) {
298 int i;
299 for ( i=sizeof( key )-1; i>=0; i-- )
300 infos += ([ key[i]: info; indent; silent; casebased ]);
301 }
302 else
303 infos += ([ key: info; indent; silent; casebased ]);
304}
305
306public varargs void AddSpecialInfo(mixed keys, string functionname,
307 string indent, mixed silent, mixed casebased )
308{
309 int i;
310 closure cl;
311
312 if(!(cl=symbol_function(functionname,this_object()))) return;
313 return AddInfo(keys,cl,indent,silent,casebased);
314}
315
316
317public void RemoveInfo( string key )
318{
319 m_delete(infos,key);
320}
321
322static varargs void _set_default_info( mixed info )
323{
324 if (pointerp(info))
325 apply(#'AddInfo/*'*/,DEFAULT_INFO,info);
326 else
327 AddInfo(DEFAULT_INFO,info);
328}
329
330public varargs mixed GetInfo(string str)
331{
332 if (!str) return deep_copy(infos);
333 return infos[str];
334}
335
336
337static mapping _query_npc_info()
338{
339 return deep_copy(infos);
340}
341
342
343static mapping _set_npc_info( mapping map_ldfied )
344{
345 if ( !mappingp(map_ldfied) )
346 return 0;
347
348 return infos = map_ldfied;
349}
350