blob: 718db888faf96629c903180bc90ba710ebc04be0 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// player/viewcmd.c -- player view command handling
4//
5// $Id: viewcmd.c 9548 2016-04-17 19:28:22Z Zesstra $
6
7#pragma strong_types
8#pragma save_types
9#pragma range_check
10#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +020011
Zesstra55f94fe2018-11-14 23:16:01 +010012//#include <player.h>
MG Mud User88f12472016-06-24 23:31:02 +020013#include <properties.h>
14#include <rooms.h>
15#include <wizlevels.h>
16#include <defines.h>
17#include <moving.h>
18#include <new_skills.h>
19#include <ansi.h>
Zesstra55f94fe2018-11-14 23:16:01 +010020#include <notify_fail.h>
MG Mud User88f12472016-06-24 23:31:02 +020021
Zesstra55f94fe2018-11-14 23:16:01 +010022#define NEED_PROTOTYPES
23#include <container.h>
24#include <thing/properties.h>
25#include <living/put_and_get.h>
26#include <living/description.h>
27#include <living/put_and_get.h>
28#include <player/command.h>
29
Zesstrabd66df72019-04-04 20:53:43 +020030//#define DEBUG(x, y, z) send_debug(x, y, z)
31#define DEBUG(x, y, z)
32
MG Mud User88f12472016-06-24 23:31:02 +020033varargs mixed More(string str, int fflag, string returnto);
34
35void create()
36{
37 Set(P_BRIEF, SAVE, F_MODE);
38 Set(P_BLIND, SAVE, F_MODE);
39}
40
41int _toggle_brief()
42{
43 int brief;
44
45 if (query_verb()=="kurz")
46 brief=1;
47 else if (query_verb()=="ultrakurz")
48 brief=2;
49 else brief=0;
50 SetProp(P_BRIEF, brief);
51 write("Du bist nun im \""+
52 (brief?(brief==1?"Kurz":"Ultrakurz"):"Lang")+"\"modus.\n");
53 return 1;
54}
55
56private int sortinv(mixed a, mixed b) { return a[0] > b[0]; }
57
58private string collectinv(mixed obj)
59{
60 if(obj[0]=="") return 0;
61 return (obj[2] ? " " : "")
62 + obj[0]
63 + (obj[1] > 1 ? " ("+obj[1]+")" : "");
64}
65
66#define I_AUTOLOAD 1
67#define I_KEEP 4
68#define I_FORMATTED 16
69#define I_ARMOUR 64
70#define I_SORT 256
71#define I_WEAPON 1024
72#define I_FORCE_SORT 4096
73#define I_NO_TABLE 16384
74
75private string getflags(string arg, int flags)
76{
77 int no, i;
78 if(sizeof(arg) < 2) return 0;
79 no = (arg[0] == '-');
80
81 for(i = 1; i < sizeof(arg); i++)
82 {
83 switch(arg[i])
84 {
85 case 'a': flags |= I_AUTOLOAD << no; break;
86 case 'b': flags |= I_KEEP << no; break;
87 case 'f': flags |= I_FORMATTED << no; break;
88 case 'r': flags |= I_ARMOUR << no; break;
89 case 's': flags |= I_SORT << no; break;
90 case 'w': flags |= I_WEAPON << no; break;
91 case 'v': flags |= (I_ARMOUR | I_WEAPON) << !no; break;
92 case '1': flags |= I_NO_TABLE; break;
93 // Die Option macht nur Aerger und kommentiert ist sie eh nicht.
94 // Wer das dringend braucht, soll Wargons Schiebepuzzle benutzen.
95 //
96 // Tiamak, 15.10.2000
97 // case 'S': flags |= I_FORCE_SORT << no; break;
98 default : return arg[i..i]; // wird ausgegeben an Spieler als unbekannt.
99 }
100 }
101 return 0;
102}
103
104static int _check_keep(object ob)
105{
106 return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
107}
108
109int _inventory(string str)
110{
111 mixed *args, output;
112 int ansi, i, flags, minv;
113 mixed inventory, weapons, armours, misc;
114 string format;
115
116 if(CannotSee()) return 1;
117
118 if((str = _unparsed_args()) && str!="")
119 {
120 string error;
121 error = "Benutzung: i[nventar] [-/+1abfrsvw]\n";
122 args = regexp(regexplode(str, "[-+][1abfrswv][1abfrswv]*"),
123 "[-+][1abfrswv][1abfrswv]*");
124 if(!sizeof(args)) return (_notify_fail(error), 0);
125 if(sizeof(args = map(args, #'getflags/*'*/, &flags) - ({ 0 })))
126 {
127 printf("%s: Unbekanntes Argument.\n"+error, implode(args, ", "));
128 return 1;
129 }
130 }
131 // Fuer Spieler gehen nur sichtbare Objekte in den Algorithmus
132 if (IS_LEARNING(ME))
133 inventory = all_inventory(ME);
134 else
135 inventory = filter_objects(all_inventory(ME), "short");
136
137 ansi = member(({"vt100", "ansi"}), QueryProp(P_TTY)) != -1;
138 minv = 1 | (flags & (I_FORMATTED | (I_FORMATTED << 1)) ? 2 : 0);
139 format = (flags & I_NO_TABLE) ? "=" : "#";
140
141// if(flags & (I_FORCE_SORT | I_FORCE_SORT << 1))
142// {
143// closure sf;
144// sf = flags & I_FORCE_SORT ? #'> : #'<;
145// s = sort_array(s, lambda(({'a, 'b}),
146// ({#'funcall, sf,
147// ({#'||,({#'call_other,'a,"short"}),""}),
148// ({#'||,({#'call_other,'b,"short"}),""})})));
149// map_objects(s, "move", this_object());
150// s = all_inventory(ME);
151// }
152
153 if (flags & I_AUTOLOAD)
154 inventory = filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
155 else if (flags & (I_AUTOLOAD << 1))
156 inventory -= filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
157
158 if(flags & I_KEEP)
159 inventory = filter(inventory,#'_check_keep);
160 else if(flags & (I_KEEP << 1))
161 inventory -= filter(inventory,#'_check_keep);
162
163 armours = filter_objects(inventory, "QueryProp", P_ARMOUR_TYPE);
164 // Kleidung dazu addieren, vorher die erkannten Ruestungen abziehen, die
165 // muessen nicht nochmal durchiteriert werden.
166 armours += filter_objects(inventory-armours, "IsClothing");
167 // Ruestungen werden hier nicht abgezogen, weil es Kram gibt, welche sowohl
168 // Ruestung als auch Waffe ist.
169 weapons = filter_objects(inventory, "QueryProp", P_WEAPON_TYPE);
170 misc = inventory - weapons - armours; // rest ;-)
171
172 if(flags & I_WEAPON)
173 {
174 inventory = weapons; misc = ({});
175 if(!(flags & (I_ARMOUR))) armours = ({});
176 }
177 if(flags & I_ARMOUR)
178 {
179 inventory = armours; misc = ({});
180 if(!(flags & I_WEAPON)) weapons = ({});
181 }
182 if(flags & (I_WEAPON << 1)) { weapons = ({}); inventory = armours + misc; }
183 if(flags & (I_ARMOUR << 1)) { armours = ({}); inventory = weapons + misc; }
184
185 output = "";
186 if(flags & (I_FORMATTED | (I_FORMATTED << 1)))
187 {
188 inventory = make_invlist(this_player(), inventory, minv);
189 if(flags & (I_SORT | (I_SORT << 1)))
190 inventory = sort_array(inventory, #'sortinv/*'*/);
191 output += sprintf("%"+format+"-78s\n",
192 implode(map(inventory,#'collectinv/*'*/),"\n"));
193 }
194 else
195 {
196 if(weapons && sizeof(weapons))
197 {
198 weapons = make_invlist(this_player(), weapons, minv);
199 if(flags & (I_SORT | (I_SORT << 1)))
200 weapons = sort_array(weapons, #'sortinv/*'*/);
201 output += (ansi?ANSI_BOLD:"") + "Waffen:" + (ansi?ANSI_NORMAL:"")+"\n"
202 + sprintf("%"+format+"-78s\n",
203 implode(map(weapons, #'collectinv/*'*/), "\n"));
204 }
205 if(armours && sizeof(armours))
206 {
207 armours = make_invlist(this_player(), armours, minv);
208 if(flags & (I_SORT | (I_SORT << 1)))
209 armours = sort_array(armours, #'sortinv/*'*/);
210 output += (ansi?ANSI_BOLD:"")
211 + "Kleidung & Ruestungen:" + (ansi?ANSI_NORMAL:"")+"\n"
212 + sprintf("%"+format+"-78s\n",
213 implode(map(armours, #'collectinv/*'*/), "\n"));
214 }
215 if(misc && sizeof(misc))
216 {
217 misc = make_invlist(this_player(), misc, minv);
218 if(flags & (I_SORT | (I_SORT << 1)))
219 misc = sort_array(misc, #'sortinv/*'*/);
220 output += (ansi?ANSI_BOLD:"") + "Verschiedenes:" + (ansi?ANSI_NORMAL:"")+"\n"
221 + sprintf("%"+format+"-78s\n",
222 implode(map(misc, #'collectinv/*'*/), "\n"));
223 }
224 }
225
226 // Spielerwunsch: 'inventar' sollte doch das Bezugsobjekt auf den Spieler
227 // aendern.
228 SetProp(P_REFERENCE_OBJECT, this_object());
229
230 if (output=="")
231 output += (ansi?ANSI_BOLD:"")+"Die Liste ist leer."+(ansi?ANSI_NORMAL:"");
232 More(output);
233 return 1;
234}
235
Zesstraa19328a2018-11-16 01:28:20 +0100236private int select_sense(string str, closure transparent_check)
MG Mud User88f12472016-06-24 23:31:02 +0200237{
Zesstra55f94fe2018-11-14 23:16:01 +0100238 int sense = -1;
MG Mud User88f12472016-06-24 23:31:02 +0200239 if(member(({"riech","rieche","schnupper","schnuppere"}),query_verb())!=-1)
240 {
241 _notify_fail("Du kannst nichts Besonderes riechen.\n");
242 sense = SENSE_SMELL;
243 }
244 else if(member(({"lausche","lausch","hoer","hoere"}),query_verb())!=-1)
245 {
246 if(QueryProp(P_DEAF))
Zesstra55f94fe2018-11-14 23:16:01 +0100247 {
248 notify_fail("Du bist taub!\n");
249 return -1;
250 }
MG Mud User88f12472016-06-24 23:31:02 +0200251 _notify_fail("Du kannst nichts Besonderes hoeren.\n");
252 sense = SENSE_SOUND;
253 }
254 else if(member(({"taste","beruehre","beruehr"}),query_verb())!=-1)
255 {
256 sense = SENSE_TOUCH;
257 // ein "ab" ganz am Ende von str wird abgeschnitten, es soll sowohl "taste
258 // x ab" als auch "taste x" funktionieren.
259 if (str) {
260 _notify_fail("Sowas kannst Du hier nicht ertasten!\n");
261 string *tmp = explode(str," ");
262 if (sizeof(tmp) > 1 && tmp[<1] == "ab")
263 str = implode(tmp[0..<2], " ");
264 }
265 else
266 _notify_fail("Was willst Du denn abtasten?\n");
267 }
268 else if (member(({"lies","lese","les"}), query_verb()) > -1)
269 {
270 _notify_fail("Was willst Du lesen?\n");
271 if ( !str ) // Kein SENSE_DEFAULT zulassen.
Zesstra55f94fe2018-11-14 23:16:01 +0100272 return -1;
273 if (CannotSee(1)) {
274 notify_fail("Du kannst nichts sehen!\n", NF_NL_MAX);
275 return -1;
MG Mud User88f12472016-06-24 23:31:02 +0200276 }
277 sense = SENSE_READ;
278 }
Zesstra55f94fe2018-11-14 23:16:01 +0100279 // else ist normales Sehen/untersuchen
280 else
281 {
282 if (CannotSee(1)) {
283 notify_fail("Du kannst nichts sehen!\n", NF_NL_MAX);
284 return -1;
285 }
286 _notify_fail("Was willst Du denn untersuchen?\n");
287 if (!str) return -1;
288 sense = SENSE_VIEW;
MG Mud User88f12472016-06-24 23:31:02 +0200289 }
Zesstra55f94fe2018-11-14 23:16:01 +0100290 // "rieche an ..." etc.: das an rausschneiden, wird nicht gebraucht.
291 if (sizeof(str))
292 {
293 if (sense!=SENSE_VIEW)
294 {
295 string what;
296 if (sscanf(str,"an %s",what)==1) str=what;
297 }
298 }
MG Mud User88f12472016-06-24 23:31:02 +0200299
Zesstraa19328a2018-11-16 01:28:20 +0100300 switch(sense)
301 {
302 // Fuer diese Sinne muss der Container offen sein.
303 case SENSE_SMELL:
304 case SENSE_TOUCH:
305 case SENSE_SOUND:
306 transparent_check = function int (object o)
307 { return o->QueryProp(P_CNT_STATUS) == CNT_STATUS_OPEN;};
308 break;
309 // Fuer alle anderen reicht, wenn er transparent ist.
310 default:
311 transparent_check = function int (object o)
Arathornc08a1e52019-01-21 18:56:32 +0100312 { return (o->QueryProp(P_TRANSPARENT) != 0); };
Zesstraa19328a2018-11-16 01:28:20 +0100313 }
314
Zesstra55f94fe2018-11-14 23:16:01 +0100315 return sense;
316}
317
Zesstraa19328a2018-11-16 01:28:20 +0100318private object get_ref_object(string *tokens, closure transparent_check,
319 int ref_given)
Zesstra55f94fe2018-11-14 23:16:01 +0100320{
321 object ref_object;
Zesstraa19328a2018-11-16 01:28:20 +0100322
323 // im Kommando speziell beschrieben ist, wo die Suche losgehen soll, spielt
324 // das Referenzobjekt keine Rolle.
325 if (sizeof(tokens) > 1 && tokens[<1] == "hier") {
326 tokens = tokens[..<2];
Zesstra55f94fe2018-11-14 23:16:01 +0100327 ref_object = environment();
Zesstraa19328a2018-11-16 01:28:20 +0100328 ref_given=1;
329 }
330 else if (sizeof(tokens) > 2 && tokens[<2] == "in")
331 {
332 if (tokens[<1] == "mir" ||
333 tokens[<1] == "dir")
334 {
335 tokens = tokens[..<3];
336 ref_object = this_object();
337 ref_given=1;
338 }
339 else if (tokens[<1] == "raum")
340 {
341 tokens = tokens[..<3];
342 ref_object = environment();
343 ref_given=1;
344 }
345 }
Zesstra55f94fe2018-11-14 23:16:01 +0100346 else
347 {
Zesstraa19328a2018-11-16 01:28:20 +0100348 // ansonsten bleibt tokens unmodifiziert und wir nehmen das
349 // Referenzobjekt (so existent).
350 ref_object = QueryProp(P_REFERENCE_OBJECT);
351 if (objectp(ref_object))
352 {
353 // Dieses muss aber "irgendwo" in unserem Umfeld sein.
354 if (ref_object == environment() || ref_object==this_object())
MG Mud User88f12472016-06-24 23:31:02 +0200355 {
Zesstraa19328a2018-11-16 01:28:20 +0100356 // Umgebung oder Spieler selber sind als Bezugsobjekt immer in
357 // Ordnung, nichts machen.
358 }
359 // Wenn es nicht wir oder environment ist, darf das Referenzobjekt
360 // nicht unsichtbar sein.
Zesstraa33e8432020-06-13 20:20:12 +0200361 else if (ref_object->QueryProp(P_INVIS))
Zesstraa19328a2018-11-16 01:28:20 +0100362 ref_object = 0;
363 // Und nur wenn das Ref-Objekt irgendwie in unserer Umgebung ist
364 // (direkt, in uns, in einem Container etc.) kommt es in Frage. Es
365 // wird daher geprueft, ob all_environments() des Ref-Objekts auch
366 // unser environment enthaelt.
367 else
368 {
Zesstra5f03eaf2018-12-17 22:40:55 +0100369 object *all_envs=all_environment(ref_object)
370 || ref_object->AllVirtualEnvironments()
371 || ({});
Zesstraa19328a2018-11-16 01:28:20 +0100372 int i=member(all_envs, environment());
373 if (i >= 0)
MG Mud User88f12472016-06-24 23:31:02 +0200374 {
Zesstraa19328a2018-11-16 01:28:20 +0100375 // dann von dem Environment an entlanglaufen und alle
376 // intermediaeren Objekt pruefen, ob sie einsehbar und sichtbar
377 // sind.
378 foreach(object env : all_envs[..i]) {
379 // Ist eine Umgebung Living oder intransparenter Container oder
380 // unsichtbar?
381 if (living(env) || !funcall(transparent_check, env)
Zesstraa33e8432020-06-13 20:20:12 +0200382 || env->QueryProp(P_INVIS))
Zesstraa19328a2018-11-16 01:28:20 +0100383 {
384 // Kette zum Ref-Objekt unterbrochen. Wenn das unterbrechende
385 // Env nicht dieses Living selber oder sein Env ist, wird das
386 // Referenzobjekt zusaetzlich genullt.
387 if (env != this_object() && env != environment())
388 ref_object = 0;
389 break;
MG Mud User88f12472016-06-24 23:31:02 +0200390 }
391 }
392 }
Zesstraa19328a2018-11-16 01:28:20 +0100393 else
394 ref_object = 0; // nicht im Raum oder Inventory
395 }
396 } // ref_object exists
MG Mud User88f12472016-06-24 23:31:02 +0200397 }
Zesstrabd66df72019-04-04 20:53:43 +0200398 DEBUG(PL, sprintf("%O\n",ref_object), "get_ref_object:");
Zesstra55f94fe2018-11-14 23:16:01 +0100399 return ref_object;
400}
MG Mud User88f12472016-06-24 23:31:02 +0200401
Zesstraa19328a2018-11-16 01:28:20 +0100402// Versucht ein Basisobjekt (ggf. in env) finden, was durch <tokens>
403// beschrieben wird.
404// Das Basisobjekt ist das, was bei einem "detail an parent" durch parent
405// gekennzeichnet wird oder notfalls das Ref-Objekt selber. Zu beachten ist,
406// dass find_base() ggf. rekursiv arbeitet, um sowas wie "riss an kerze in
407// kerzenstaender auf tisch" zu finden. detail ist dann der letzte Teil der
408// tokens (der Anfang!), was keinen Container mehr bezeichnet, das gefundene
409// Objekt ist der letzte Container/Item auf dem Weg zu detail.
410// detail muss als Referenz uebergeben werden.
411// transparent_check ist die Closure, die prueft, ob man in einen
412// Container reinforschen (sehen, lesen, riechen, tasten etc.) kann. Sie
413// bekommt den Container uebergeben.
414private object find_base(object env, string *tokens, string detail,
415 closure transparent_check)
Zesstra55f94fe2018-11-14 23:16:01 +0100416{
Zesstraa19328a2018-11-16 01:28:20 +0100417 // zuerst pruefen, ob man in env reinsehen/reinforschen kann (in
418 // this_object() und environment() kann man immer reinsehen).
419 if (env && env != this_object() && env != environment())
MG Mud User88f12472016-06-24 23:31:02 +0200420 {
Zesstraa19328a2018-11-16 01:28:20 +0100421 // Nicht in Lebewesen reingucken und nicht in intransparente/geschlossene
422 // Container
423 if (living(env) || !funcall(transparent_check, env))
MG Mud User88f12472016-06-24 23:31:02 +0200424 {
Zesstraa19328a2018-11-16 01:28:20 +0100425 // suche nach Objekten in env ist jetzt zuende, weil nicht in env
426 // reinsehbar.
427 // Aber tokens koennte noch ein detail beschreiben, was der Aufrufer an
428 // env findet. Also tokens nach detail und env zurueckgeben.
429 detail = implode(tokens, " ");
Zesstrabd66df72019-04-04 20:53:43 +0200430 DEBUG(PL, sprintf("Env: %O, Detail: %s\n",env, detail),
431 "find_base 1");
Zesstraa19328a2018-11-16 01:28:20 +0100432 return env;
MG Mud User88f12472016-06-24 23:31:02 +0200433 }
MG Mud User88f12472016-06-24 23:31:02 +0200434 }
Zesstraa19328a2018-11-16 01:28:20 +0100435 // ansonsten schauen, ob in tokens noch ein (weiterer) Container beschrieben
436 // wird.
437 for (int i = sizeof(tokens)-1; i > 1; i--)
Zesstra55f94fe2018-11-14 23:16:01 +0100438 {
Zesstradc0ef892018-11-18 23:36:03 +0100439 object ob, *objs;
Zesstraa19328a2018-11-16 01:28:20 +0100440 // In env oder mir oder umgebung gucken, ob ein passendes Objekt da ist.
441 // Hierfuer werden mit jedem Schleifendurchlauf von hinten mehr worte aus
442 // tokens benutzt. (Beispiel "muenze in boerse 2")
Zesstradc0ef892018-11-18 23:36:03 +0100443 // present waere schneller und die Komplexitaet von present_objects waere
444 // hier nicht noetig. Allerdings beruecksichtigt es die Sichtbarkeit und
445 // ggf. virtuell anwesende Objekte, was present nicht tut.
446 string suchid=implode(tokens[i..], " ");
Zesstraa19328a2018-11-16 01:28:20 +0100447 if (env)
Zesstradc0ef892018-11-18 23:36:03 +0100448 objs = env->present_objects(suchid) || ({});
Zesstraa19328a2018-11-16 01:28:20 +0100449 else
Zesstradc0ef892018-11-18 23:36:03 +0100450 objs = environment()->present_objects(suchid) ||
451 this_object()->present_objects(suchid);
452 // wenn nicht genau ein Objekt gefunden wird, wird weitergesucht, im
453 // naechsten Durchlauf mit einem Wort mehr aus tokens
Zesstraa81a17b2018-12-03 21:25:21 +0100454 if (!sizeof(objs))
Zesstradc0ef892018-11-18 23:36:03 +0100455 {
Zesstraa81a17b2018-12-03 21:25:21 +0100456 continue;
457 }
Zesstra9298c2d2018-12-03 21:32:59 +0100458 // falls mehr als ein Objekt gefunden wurden: das erste nehmen, wie
459 // ueberall sonst auch.
Zesstraa81a17b2018-12-03 21:25:21 +0100460 ob = objs[0];
Zesstraa19328a2018-11-16 01:28:20 +0100461
462 // an dieser Stelle wird (noch) nicht geprueft, ob man in das gefundene
463 // Objekt hineinforschen kann.
464
465 // Wenn passende Praeposition davor (z.B. "in" (beutel), "auf" (tisch)),
466 // rekursiv weitersuchen, ob der restliche Teil von tokens evtl. nochmal
467 // nen container in dem gefundenen Objekt bezeichnet.
468 if (tokens[i-1] == ob->QueryProp(P_PREPOSITION))
469 return find_base(ob, tokens[..i-2], &detail, transparent_check);
470
471 // Wenn tokens[i-1] "an" oder "am" ist, wird zwar nicht weiter verzweigt,
472 // aber vermutlich wird ein Detail an diesem Objekt gesucht, d.h. das
473 // Objekt wird env, tokens entsprechend gekuerzt und die Schleife beendet.
474 // Bemerkung: haette ob tatsaechlich eine Praeposition an|am gehabt,
475 // waeren wir vorher in find_base() verzweigt zum Suchen in dem Container.
476 if (tokens[i-1] == "an" || tokens[i-1] == "am") {
477 env = ob;
478 tokens=tokens[..i-2];
479 break;
480 }
Zesstra55f94fe2018-11-14 23:16:01 +0100481 }
Zesstraa19328a2018-11-16 01:28:20 +0100482 // an diesem Punkt sind in tokens nur noch worte drin, die keinen Container
483 // mehr beschreiben. Die Resttokens kommen nach <detail>, sie beschreiben
484 // dann entweder ein Objekt oder ein Detail.
485 detail = implode(tokens, " ");
486
487 // Wenn es ein env gibt, beschreibt detail nun ein Objekt in env oder ein
488 // Detail an env, was der Aufrufer prueft.
489 // Wenn es aber kein env gibt (das passiert, wenn der Aufrufer dieser
490 // Funktion schon kein env (->Referenzobjekt) hereingegeben hat, wurde auch
491 // kein Container gefunden, der durch die tokens beschrieben wurde. Dann
492 // kann der Aufrufer noch an/in this_object() oder environment() gucken, ob
493 // was zu finden ist.
Zesstrabd66df72019-04-04 20:53:43 +0200494 DEBUG(PL, sprintf("Env: %O, Detail: %s\n",env, detail),
495 "find_base 2:");
Zesstraa19328a2018-11-16 01:28:20 +0100496 return env;
Zesstra55f94fe2018-11-14 23:16:01 +0100497}
MG Mud User88f12472016-06-24 23:31:02 +0200498
Zesstra55f94fe2018-11-14 23:16:01 +0100499private nosave int exa_cnt;
500private nosave int exa_time;
501private nosave string *exa;
502private void unt_script_dings(string str)
503{
504 // unt-script-sucher
505 if (!sizeof(str)) return;
506 if (member(({"boden","decke","wand","waende"}),old_explode(str," ")[0]) == -1) {
507 exa_cnt -= (time() - exa_time)/2;
508 exa_time = time();
509 exa_cnt++;
510 if (!exa)
511 exa = ({ str });
512 else
513 exa += ({ str });
514 if (exa_cnt > 10) {
515 log_file("ARCH/LOOK",
516 sprintf("%s: %s in %s\n%@O\n",dtime(time()),getuid(this_object()),
517 environment() ? object_name(environment()) : "???",exa), 150000);
518 exa_cnt = 0;
519 exa = ({});
520 }
521 else if (exa_cnt < 0) {
522 exa_cnt = 0;
523 exa = ({});
524 }
525 }
526}
527
Zesstraf5621a22018-12-06 21:27:21 +0100528private string nf_meldung(string detail, int sense)
Zesstra481215f2018-12-05 22:15:26 +0100529{
530 switch(sense)
531 {
532 case SENSE_SMELL:
Zesstraf5621a22018-12-06 21:27:21 +0100533 return "Du riechst nichts Besonderes.\n";
Zesstra481215f2018-12-05 22:15:26 +0100534 case SENSE_SOUND:
Zesstraf5621a22018-12-06 21:27:21 +0100535 return "Du hoerst nichts Besonderes.\n";
Zesstra481215f2018-12-05 22:15:26 +0100536 case SENSE_TOUCH:
Zesstraf5621a22018-12-06 21:27:21 +0100537 return "Du fuehlst nichts Besonderes.\n";
Zesstra481215f2018-12-05 22:15:26 +0100538// case SENSE_READ:
539// default:
540// return "siehst";
541 }
Zesstraf5621a22018-12-06 21:27:21 +0100542 return sprintf("\'%s\' siehst Du da nicht!\n",capitalize(detail));
Zesstra481215f2018-12-05 22:15:26 +0100543}
544
Zesstrafc501e12018-12-02 23:37:33 +0100545private int _examine_rec(object *ref_objects, string *tokens, closure
Zesstraa19328a2018-11-16 01:28:20 +0100546 transparent_check, int sense, int mode,
547 int ref_given)
548{
Zesstrafc501e12018-12-02 23:37:33 +0100549 // ref_objects sind alle in Frage kommenden Kandidaten, an denen wir suchen.
Zesstrabd66df72019-04-04 20:53:43 +0200550 DEBUG(PL, sprintf("Refobjekt-Kandidaten: %O\n",ref_objects),
551 "_examine_rec 1:");
Zesstrafc501e12018-12-02 23:37:33 +0100552
Zesstraa19328a2018-11-16 01:28:20 +0100553 // Sodann ein Basisobjekt finden. Das Basisobjekt ist das, was bei einem
554 // "detail an parent" durch parent gekennzeichnet wird oder notfalls das
555 // Ref-Objekt von oben. Zu beachten ist, dass find_base() ggf. rekursiv
556 // arbeitet, um sowas wie "riss an kerze in kerzenstaender auf tisch" zu
557 // finden. detail ist dann das letzte Teil der tokens, der keinen Container
558 // mehr bezeichnet, das gefundene Objekt ist der letzte Container/Item auf
559 // dem Weg zu detail. (detail==riss, base==kerze)
560 string detail; // detail an parent
561 // das fuer parent gefundene objekt
Zesstrafc501e12018-12-02 23:37:33 +0100562 object base = find_base(ref_objects[0], tokens, &detail, transparent_check);
563 // BTW: find_base liefert immer was zurueck - wenn nix gefunden wird, das
564 // ref_object, was als Start der Suche uebergeben wurde.
Zesstraa19328a2018-11-16 01:28:20 +0100565 // Jetzt wird versucht, ein Objekt mit der ID "detail" in base oder
Zesstrafc501e12018-12-02 23:37:33 +0100566 // ein Detail an base zu finden.
567
Zesstraa19328a2018-11-16 01:28:20 +0100568 object *objs;
Zesstrafc501e12018-12-02 23:37:33 +0100569 // Wenn wir in base reingucken koennen, ermitteln wir alle zu
Zesstraa19328a2018-11-16 01:28:20 +0100570 // detail in Frage kommenanden Objekte in seinem Inventar
Zesstrafc501e12018-12-02 23:37:33 +0100571 if (base == this_object() || base == environment() ||
572 (funcall(transparent_check, base) && !living(base)))
Zesstraa19328a2018-11-16 01:28:20 +0100573 {
Zesstrafc501e12018-12-02 23:37:33 +0100574 objs = base->locate_objects(detail, 1) || ({});
Zesstraa19328a2018-11-16 01:28:20 +0100575 }
Zesstraa19328a2018-11-16 01:28:20 +0100576 else
577 {
Zesstrafc501e12018-12-02 23:37:33 +0100578 // Basisobjekt da, aber nicht reinguckbar, also keine Objekte
579 // gefunden. base aber nicht nullen, denn es ist ja noch gueltig fuer
580 // Detailsuchen an base...
581 objs = ({});
Zesstraa19328a2018-11-16 01:28:20 +0100582 }
583 // Und in jedem Fall werden alle Objekt raussortiert, die unsichtbar sind.
584 // ;-)
Zesstraa33e8432020-06-13 20:20:12 +0200585 objs -= filter_objects(objs, "QueryProp", P_INVIS);
Zesstraa19328a2018-11-16 01:28:20 +0100586
587 // Wenn es sichtbare gibt, werden die ggf. angeguckt.
588 if (sizeof(objs))
589 {
590 // Objekte gefunden, mal schauen, ob die taugen (d.h. fuer den jew. Sinn
591 // Infos haben. Wenn nicht, muessen wir weitersuchen.
592 // Aber erstmal die Objekte durchlaufen.
593 foreach(object ob: objs)
594 {
595 string out;
596 if (sense == SENSE_VIEW)
597 {
598 out = ob->long(mode);
599 }
600 else if (sense == SENSE_READ)
601 {
602 // Extrawurst: P_READ_MSG auch noch abfragen.
603 out = ob->QueryProp(P_READ_MSG);
604 if (!stringp(out))
605 out = ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),SENSE_READ);
606 }
607 else
608 out=ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),sense);
609 // Wenn was gefunden wurde, sind wir fertig.
610 if (stringp(out))
611 {
612 SetProp(P_REFERENCE_OBJECT, ob);
613 tell_object(ME, out);
614 return 1;
615 }
616 }
617 }
618 // keine Objekte gefunden oder die hatten nix fuer unseren Sinn
619 // dabei. Also nach ordinaeren Details an base/parent suchen.
620 string out = base->GetDetail(detail, QueryProp(P_REAL_RACE), sense);
621 // Achja, Tueren gibt es ja auch noch. :-o (TODO: die solten vItems werden)
622 if (!out && sense==SENSE_VIEW)
623 out = base->GetDoorDesc(detail);
624 if (out)
625 {
626 // Detail gefunden, base darf neues Referenzobjekt werden und nach
627 // Ausgabe sind wir fertig.
628 SetProp(P_REFERENCE_OBJECT, base);
629 write(out);
630 return 1;
631 }
632 else
633 {
634 // wenn auch keine Details gefunden, dann schauen, unser Env evtl.
635 // noch nen passendes Detail oder Objekt hat. Das ist aber natuerlich
636 // nur der Fall, wenn ref_object nicht eh schon das Environment war.
637 // Ausserdem nicht machen, wenn der Nutzer ein spezifisches Referenzobjekt
638 // angegeben hat (z.B. "in raum").
Zesstrafc501e12018-12-02 23:37:33 +0100639 if (sizeof(ref_objects) > 1)
Zesstraa19328a2018-11-16 01:28:20 +0100640 {
Zesstrafc501e12018-12-02 23:37:33 +0100641 // Wir schauen uns ggf. weitere Kandidaten an. Bei den anderen
Zesstraa19328a2018-11-16 01:28:20 +0100642 // Sinnen kein neues notify_fail, weil sonst staendig Meldungen kommen,
643 // dass x nicht da ist, nur weil es keine Beschreibung fuer den Sinn hat.
Zesstraefc5b302018-12-14 02:00:58 +0100644 if (sense==SENSE_VIEW || sense==SENSE_READ)
Zesstrafc501e12018-12-02 23:37:33 +0100645 {
Zesstra9a083752018-12-05 20:18:23 +0100646 // Ausgabe, was gerade angeguckt hat.
647 if (ref_objects[0] == environment())
648 out="Sowas siehst Du hier nicht. ";
649 else if (ref_objects[0] == this_object())
650 out="Sowas siehst Du an Dir nicht. ";
651 else
652 out="Sowas siehst Du an/in "+ref_objects[0]->name(WEM) +" nicht. ";
653 // Ausgabe, was man als naechstes anguckt.
654 if (ref_objects[1] == environment())
655 out+="Du schaust Dich um.";
656 else
657 out+="Du schaust Dich an.";
658
659 write(break_string(out,78));
Zesstrafc501e12018-12-02 23:37:33 +0100660 }
661 return _examine_rec(ref_objects[1..], tokens, transparent_check,
662 sense, mode, 0);
Zesstraa19328a2018-11-16 01:28:20 +0100663 }
664 // Leider ist nix mehr uebrig zum angucken und in jedem Fall Ende.
665 else
666 {
667 // Wenn spezifisches ref_object vom Nutzer angegeben (ref_given!=0),
668 // gibt es eine leicht andere Meldung und das Bezugsobjekt muss weg.
Zesstraa19328a2018-11-16 01:28:20 +0100669 if (ref_given) {
670 SetProp(P_REFERENCE_OBJECT, 0);
Zesstraf5621a22018-12-06 21:27:21 +0100671 notify_fail(break_string(nf_meldung(detail, sense),78));
Zesstraa19328a2018-11-16 01:28:20 +0100672 }
Zesstra9ed8c9b2018-12-03 21:23:56 +0100673 // Ansonsten gibt es nur nen passendes notify_fail und Ende.
Zesstraa19328a2018-11-16 01:28:20 +0100674 else if (sense==SENSE_VIEW) {
Zesstraf5621a22018-12-06 21:27:21 +0100675 notify_fail(break_string("\'" + capitalize(detail) + "\'"
676 + " siehst Du auch da nicht!\n"),78);
Zesstra9ed8c9b2018-12-03 21:23:56 +0100677 }
678 else {
Zesstraf5621a22018-12-06 21:27:21 +0100679 notify_fail(break_string(nf_meldung(detail, sense),78));
Zesstraa19328a2018-11-16 01:28:20 +0100680 }
681 return 0;
682 }
683 }
684 // Nie erreicht.
685 return 0;
686}
687
Zesstra55f94fe2018-11-14 23:16:01 +0100688varargs int _examine(string str, int mode)
689{
690 // Sinn auswaehlen, der benutzt wird. Wenn -1, abbrechen (kein Sinn bzw.
691 // Sinn erlaubt ggf. kein leeren <str> fuer Defaulmeldung). Je nach Sinn
Zesstraa19328a2018-11-16 01:28:20 +0100692 // wird auch <str> modifiziert.
693 // transparent_check enthaelt spaeter die Closure, die entscheidet,
694 // ob man mit dem Sinn in einen Container reinforschen kann.
695 closure transparent_check;
696 int sense=select_sense(&str, &transparent_check);
Zesstra55f94fe2018-11-14 23:16:01 +0100697 if (sense<0) return 0;
698
699 unt_script_dings(str);
700
701 // Wenn kein str, dann SENSE_DEFAULT vom Environment ausgeben. Bei
Zesstraa19328a2018-11-16 01:28:20 +0100702 // SENSE_VIEW wird das bereits bei den Aufrufern dieser Funktion gemacht und
703 // die Beschreibung der Umgebung ausgegeben.
Zesstra55f94fe2018-11-14 23:16:01 +0100704 if (sense!=SENSE_VIEW)
705 {
706 if (!str)
707 {
708 string detail = environment()->GetDetail(SENSE_DEFAULT,
709 QueryProp(P_REAL_RACE),sense);
710 if(!detail)
711 return 0;
712 write(detail);
713 return 1;
714 }
715 }
716
Zesstraa19328a2018-11-16 01:28:20 +0100717 string *tokens = explode(str, " ");
718 int ref_given;
719 // Das Ref-Objekt finden, das kann dann auch in tokens angegeben sein. In dem
720 // Fall wird die Liste an tokens um die Angabe des Ref-Objekts gekuerzt und
721 // ref_given gesetzt.
Zesstrafc501e12018-12-02 23:37:33 +0100722 object ref_ob = get_ref_object(&tokens, transparent_check, &ref_given);
Zesstraa19328a2018-11-16 01:28:20 +0100723 // Bemerkung: Das Referenzobjekt ist garantiert sichtbar, aber nicht
724 // unbedingt einsehbar. Wird weiter unten geprueft.
Zesstra55f94fe2018-11-14 23:16:01 +0100725
Zesstrafc501e12018-12-02 23:37:33 +0100726 // Falls nicht explizit ein Objekt als Ref-Objekt angegeben wurde, werden
727 // max. 3 Objekte angeguckt: das eigentliche Referenzobjekt, das
728 // Environment und das eigene Objekt. Da ref_object auch eines der letzten
729 // beiden sein kann, muss die Dublette ggf. raus.
730 object *ref_objects;
731 if (ref_ob)
732 {
733 ref_objects=({ref_ob});
734 if (!ref_given)
735 ref_objects += ({environment(), this_object()}) - ref_objects;
736
737 }
738 else
739 ref_objects = ({environment(), this_object()});
740
741 return _examine_rec(ref_objects, tokens, transparent_check, sense, mode,
742 ref_given);
MG Mud User88f12472016-06-24 23:31:02 +0200743}
744
Zesstra55f94fe2018-11-14 23:16:01 +0100745// Funktion fuer "schau in ..."
MG Mud User88f12472016-06-24 23:31:02 +0200746varargs int look_into(string str,int mode)
747{
748 object *found_obs;
749
750 if( CannotSee() ) return 1;
Zesstra55f94fe2018-11-14 23:16:01 +0100751 _notify_fail("Wo willst Du denn reinschauen?\n");
752 found_obs=find_objects(str, 0, 0);
753 if (!sizeof(found_obs))
MG Mud User88f12472016-06-24 23:31:02 +0200754 {
755 if (environment() &&
756 (environment()->GetDetail(str,QueryProp(P_REAL_RACE))||
757 environment()->GetDoorDesc(str)))
758 _notify_fail("Da kannst Du so nicht reinsehen.\n");
759 return 0;
760 }
Zesstra55f94fe2018-11-14 23:16:01 +0100761
MG Mud User88f12472016-06-24 23:31:02 +0200762 return _examine(str, mode);
763}
764
765/* Gebe die Umgebung des aktiven Spielers zurueck, lasse dabei */
766/* rekursiv geschachtelte Raeume zu. */
767/* Wenn allow_short 0 ist, so wird immer die long-descr benutzt */
768varargs string env_descr(int allow_short,int flags, int force_short )
769{
770 object env;
771 int brief;
772
773 env = environment(ME);
774
775 if(!env)
776 return "Du schwebst im Nichts ... Du siehst nichts, rein gar nichts ...\n";
777
778 if (!force_short && (!allow_short || !QueryProp(P_BRIEF)))
779 return env->int_long(ME,ME,flags);
780
781 if (!flags && ((brief=QueryProp(P_BRIEF))>=2))
782 return "";
783
784 return env->int_short(ME,ME);
785}
786
Zesstra55f94fe2018-11-14 23:16:01 +0100787// Kommandofunktion fuer schau.
788// Verzweigt ggf. in _examine() fuers normale Untersuchen.
MG Mud User88f12472016-06-24 23:31:02 +0200789int _look(string str)
790{
791 string s;
792 int flag;
793
794 if(CannotSee()) return 1;
795
Zesstra55f94fe2018-11-14 23:16:01 +0100796 // nur schau mit ggf. Flags entsorgt das Ref-Objekt und schaut die
797 // Raumbeschreibung an.
MG Mud User88f12472016-06-24 23:31:02 +0200798 if(!str)
799 {
800 SetProp(P_REFERENCE_OBJECT, 0);
801 write( env_descr() );
802 return 1;
803 }
804 if(str=="-f" || str=="genau")
805 {
806 SetProp(P_REFERENCE_OBJECT, 0);
807 write( env_descr(0,2) );
808 return 1;
809 }
810 if(str=="-k" || str=="kurz")
811 {
812 SetProp(P_REFERENCE_OBJECT, 0);
813 write( env_descr(1,2,1) );
814 return 1;
815 }
Zesstra55f94fe2018-11-14 23:16:01 +0100816 // Ansonsten wird in _examine() weitergemacht, wobei Flags vorher
817 // rausgeschnitten werden. _examine() benutzt die Flags, falls es eine
818 // Langbeschreibung eines Objektes ausgeben wird.
MG Mud User88f12472016-06-24 23:31:02 +0200819 if(str[0..2]=="-f "){
820 flag=2;
821 str=str[3..];
822 }
823 else if(str[0..5]=="genau "){
824 flag=2;
825 str=str[6..];
826 }
827 else flag = 0;
Zesstra55f94fe2018-11-14 23:16:01 +0100828
MG Mud User88f12472016-06-24 23:31:02 +0200829 if (sscanf(str,"%s an",s)) str=s;
Zesstra55f94fe2018-11-14 23:16:01 +0100830 // "in mir", "in dir" soll in _examine rein, aber "in ..." in look_into().
MG Mud User88f12472016-06-24 23:31:02 +0200831 if (sscanf(str,"%s in mir",s)||sscanf(str,"%s in dir",s)) return _examine(str,flag);
832 if (sscanf(str,"in %s",s)) return look_into(s,flag);
Zesstra55f94fe2018-11-14 23:16:01 +0100833 // Alles andere weiter an _examine().
MG Mud User88f12472016-06-24 23:31:02 +0200834 return _examine(str,flag);
835}
836
837int _equipment(string arg)
838{
839 if (CannotSee()) return 1;
840 call_other("/std/player/invmaster/invmaster", "ShowInv", ME, arg);
841 return 1;
842}
843
844static mixed _query_localcmds()
845{
846 return
847 ({({"ausruestung", "_equipment",0,0}),
848 ({"i","_inventory",0,0}),
849 ({"inv","_inventory",0,0}),
850 ({"inventur","_inventory",0,0}),
851 ({"schau","_look",0,0}),
852 ({"schaue","_look",0,0}),
853 ({"unt","_examine",0,0}),
854 ({"untersuch","_examine",0,0}),
855 ({"betracht","_examine",0,0}),
856 ({"untersuche","_examine",0,0}),
857 ({"betrachte","_examine",0,0}),
858 ({"betr","_examine",0,0}),
Zesstra55f94fe2018-11-14 23:16:01 +0100859 ({"lausche","_examine",0,0}),
860 ({"lausch","_examine",0,0}),
861 ({"hoer","_examine",0,0}),
862 ({"hoere","_examine",0,0}),
863 ({"lies","_examine",0,0}),
864 ({"lese","_examine",0,0}),
865 ({"les","_examine",0,0}),
866 ({"schnupper","_examine",0,0}),
867 ({"schnuppere","_examine",0,0}),
868 ({"riech","_examine",0,0}),
869 ({"rieche","_examine",0,0}),
870 ({"taste","_examine",0,0}),
871 ({"beruehre","_examine",0,0}),
872 ({"beruehr","_examine",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +0200873 ({"kurz","_toggle_brief",0,0}),
874 ({"lang","_toggle_brief",0,0}),
875 ({"ultrakurz","_toggle_brief",0,0})
876 });
877}