blob: 3d278f179647568a352d24d1fb9b14ef205d84d7 [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
11#pragma pedantic
12
Zesstra55f94fe2018-11-14 23:16:01 +010013//#include <player.h>
MG Mud User88f12472016-06-24 23:31:02 +020014#include <properties.h>
15#include <rooms.h>
16#include <wizlevels.h>
17#include <defines.h>
18#include <moving.h>
19#include <new_skills.h>
20#include <ansi.h>
Zesstra55f94fe2018-11-14 23:16:01 +010021#include <notify_fail.h>
MG Mud User88f12472016-06-24 23:31:02 +020022#include <sys_debug.h>
23
Zesstra55f94fe2018-11-14 23:16:01 +010024#define NEED_PROTOTYPES
25#include <container.h>
26#include <thing/properties.h>
27#include <living/put_and_get.h>
28#include <living/description.h>
29#include <living/put_and_get.h>
30#include <player/command.h>
31
MG Mud User88f12472016-06-24 23:31:02 +020032varargs mixed More(string str, int fflag, string returnto);
33
34void create()
35{
36 Set(P_BRIEF, SAVE, F_MODE);
37 Set(P_BLIND, SAVE, F_MODE);
38}
39
40int _toggle_brief()
41{
42 int brief;
43
44 if (query_verb()=="kurz")
45 brief=1;
46 else if (query_verb()=="ultrakurz")
47 brief=2;
48 else brief=0;
49 SetProp(P_BRIEF, brief);
50 write("Du bist nun im \""+
51 (brief?(brief==1?"Kurz":"Ultrakurz"):"Lang")+"\"modus.\n");
52 return 1;
53}
54
55private int sortinv(mixed a, mixed b) { return a[0] > b[0]; }
56
57private string collectinv(mixed obj)
58{
59 if(obj[0]=="") return 0;
60 return (obj[2] ? " " : "")
61 + obj[0]
62 + (obj[1] > 1 ? " ("+obj[1]+")" : "");
63}
64
65#define I_AUTOLOAD 1
66#define I_KEEP 4
67#define I_FORMATTED 16
68#define I_ARMOUR 64
69#define I_SORT 256
70#define I_WEAPON 1024
71#define I_FORCE_SORT 4096
72#define I_NO_TABLE 16384
73
74private string getflags(string arg, int flags)
75{
76 int no, i;
77 if(sizeof(arg) < 2) return 0;
78 no = (arg[0] == '-');
79
80 for(i = 1; i < sizeof(arg); i++)
81 {
82 switch(arg[i])
83 {
84 case 'a': flags |= I_AUTOLOAD << no; break;
85 case 'b': flags |= I_KEEP << no; break;
86 case 'f': flags |= I_FORMATTED << no; break;
87 case 'r': flags |= I_ARMOUR << no; break;
88 case 's': flags |= I_SORT << no; break;
89 case 'w': flags |= I_WEAPON << no; break;
90 case 'v': flags |= (I_ARMOUR | I_WEAPON) << !no; break;
91 case '1': flags |= I_NO_TABLE; break;
92 // Die Option macht nur Aerger und kommentiert ist sie eh nicht.
93 // Wer das dringend braucht, soll Wargons Schiebepuzzle benutzen.
94 //
95 // Tiamak, 15.10.2000
96 // case 'S': flags |= I_FORCE_SORT << no; break;
97 default : return arg[i..i]; // wird ausgegeben an Spieler als unbekannt.
98 }
99 }
100 return 0;
101}
102
103static int _check_keep(object ob)
104{
105 return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
106}
107
108int _inventory(string str)
109{
110 mixed *args, output;
111 int ansi, i, flags, minv;
112 mixed inventory, weapons, armours, misc;
113 string format;
114
115 if(CannotSee()) return 1;
116
117 if((str = _unparsed_args()) && str!="")
118 {
119 string error;
120 error = "Benutzung: i[nventar] [-/+1abfrsvw]\n";
121 args = regexp(regexplode(str, "[-+][1abfrswv][1abfrswv]*"),
122 "[-+][1abfrswv][1abfrswv]*");
123 if(!sizeof(args)) return (_notify_fail(error), 0);
124 if(sizeof(args = map(args, #'getflags/*'*/, &flags) - ({ 0 })))
125 {
126 printf("%s: Unbekanntes Argument.\n"+error, implode(args, ", "));
127 return 1;
128 }
129 }
130 // Fuer Spieler gehen nur sichtbare Objekte in den Algorithmus
131 if (IS_LEARNING(ME))
132 inventory = all_inventory(ME);
133 else
134 inventory = filter_objects(all_inventory(ME), "short");
135
136 ansi = member(({"vt100", "ansi"}), QueryProp(P_TTY)) != -1;
137 minv = 1 | (flags & (I_FORMATTED | (I_FORMATTED << 1)) ? 2 : 0);
138 format = (flags & I_NO_TABLE) ? "=" : "#";
139
140// if(flags & (I_FORCE_SORT | I_FORCE_SORT << 1))
141// {
142// closure sf;
143// sf = flags & I_FORCE_SORT ? #'> : #'<;
144// s = sort_array(s, lambda(({'a, 'b}),
145// ({#'funcall, sf,
146// ({#'||,({#'call_other,'a,"short"}),""}),
147// ({#'||,({#'call_other,'b,"short"}),""})})));
148// map_objects(s, "move", this_object());
149// s = all_inventory(ME);
150// }
151
152 if (flags & I_AUTOLOAD)
153 inventory = filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
154 else if (flags & (I_AUTOLOAD << 1))
155 inventory -= filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
156
157 if(flags & I_KEEP)
158 inventory = filter(inventory,#'_check_keep);
159 else if(flags & (I_KEEP << 1))
160 inventory -= filter(inventory,#'_check_keep);
161
162 armours = filter_objects(inventory, "QueryProp", P_ARMOUR_TYPE);
163 // Kleidung dazu addieren, vorher die erkannten Ruestungen abziehen, die
164 // muessen nicht nochmal durchiteriert werden.
165 armours += filter_objects(inventory-armours, "IsClothing");
166 // Ruestungen werden hier nicht abgezogen, weil es Kram gibt, welche sowohl
167 // Ruestung als auch Waffe ist.
168 weapons = filter_objects(inventory, "QueryProp", P_WEAPON_TYPE);
169 misc = inventory - weapons - armours; // rest ;-)
170
171 if(flags & I_WEAPON)
172 {
173 inventory = weapons; misc = ({});
174 if(!(flags & (I_ARMOUR))) armours = ({});
175 }
176 if(flags & I_ARMOUR)
177 {
178 inventory = armours; misc = ({});
179 if(!(flags & I_WEAPON)) weapons = ({});
180 }
181 if(flags & (I_WEAPON << 1)) { weapons = ({}); inventory = armours + misc; }
182 if(flags & (I_ARMOUR << 1)) { armours = ({}); inventory = weapons + misc; }
183
184 output = "";
185 if(flags & (I_FORMATTED | (I_FORMATTED << 1)))
186 {
187 inventory = make_invlist(this_player(), inventory, minv);
188 if(flags & (I_SORT | (I_SORT << 1)))
189 inventory = sort_array(inventory, #'sortinv/*'*/);
190 output += sprintf("%"+format+"-78s\n",
191 implode(map(inventory,#'collectinv/*'*/),"\n"));
192 }
193 else
194 {
195 if(weapons && sizeof(weapons))
196 {
197 weapons = make_invlist(this_player(), weapons, minv);
198 if(flags & (I_SORT | (I_SORT << 1)))
199 weapons = sort_array(weapons, #'sortinv/*'*/);
200 output += (ansi?ANSI_BOLD:"") + "Waffen:" + (ansi?ANSI_NORMAL:"")+"\n"
201 + sprintf("%"+format+"-78s\n",
202 implode(map(weapons, #'collectinv/*'*/), "\n"));
203 }
204 if(armours && sizeof(armours))
205 {
206 armours = make_invlist(this_player(), armours, minv);
207 if(flags & (I_SORT | (I_SORT << 1)))
208 armours = sort_array(armours, #'sortinv/*'*/);
209 output += (ansi?ANSI_BOLD:"")
210 + "Kleidung & Ruestungen:" + (ansi?ANSI_NORMAL:"")+"\n"
211 + sprintf("%"+format+"-78s\n",
212 implode(map(armours, #'collectinv/*'*/), "\n"));
213 }
214 if(misc && sizeof(misc))
215 {
216 misc = make_invlist(this_player(), misc, minv);
217 if(flags & (I_SORT | (I_SORT << 1)))
218 misc = sort_array(misc, #'sortinv/*'*/);
219 output += (ansi?ANSI_BOLD:"") + "Verschiedenes:" + (ansi?ANSI_NORMAL:"")+"\n"
220 + sprintf("%"+format+"-78s\n",
221 implode(map(misc, #'collectinv/*'*/), "\n"));
222 }
223 }
224
225 // Spielerwunsch: 'inventar' sollte doch das Bezugsobjekt auf den Spieler
226 // aendern.
227 SetProp(P_REFERENCE_OBJECT, this_object());
228
229 if (output=="")
230 output += (ansi?ANSI_BOLD:"")+"Die Liste ist leer."+(ansi?ANSI_NORMAL:"");
231 More(output);
232 return 1;
233}
234
Zesstraa19328a2018-11-16 01:28:20 +0100235private int select_sense(string str, closure transparent_check)
MG Mud User88f12472016-06-24 23:31:02 +0200236{
Zesstra55f94fe2018-11-14 23:16:01 +0100237 int sense = -1;
MG Mud User88f12472016-06-24 23:31:02 +0200238 if(member(({"riech","rieche","schnupper","schnuppere"}),query_verb())!=-1)
239 {
240 _notify_fail("Du kannst nichts Besonderes riechen.\n");
241 sense = SENSE_SMELL;
242 }
243 else if(member(({"lausche","lausch","hoer","hoere"}),query_verb())!=-1)
244 {
245 if(QueryProp(P_DEAF))
Zesstra55f94fe2018-11-14 23:16:01 +0100246 {
247 notify_fail("Du bist taub!\n");
248 return -1;
249 }
MG Mud User88f12472016-06-24 23:31:02 +0200250 _notify_fail("Du kannst nichts Besonderes hoeren.\n");
251 sense = SENSE_SOUND;
252 }
253 else if(member(({"taste","beruehre","beruehr"}),query_verb())!=-1)
254 {
255 sense = SENSE_TOUCH;
256 // ein "ab" ganz am Ende von str wird abgeschnitten, es soll sowohl "taste
257 // x ab" als auch "taste x" funktionieren.
258 if (str) {
259 _notify_fail("Sowas kannst Du hier nicht ertasten!\n");
260 string *tmp = explode(str," ");
261 if (sizeof(tmp) > 1 && tmp[<1] == "ab")
262 str = implode(tmp[0..<2], " ");
263 }
264 else
265 _notify_fail("Was willst Du denn abtasten?\n");
266 }
267 else if (member(({"lies","lese","les"}), query_verb()) > -1)
268 {
269 _notify_fail("Was willst Du lesen?\n");
270 if ( !str ) // Kein SENSE_DEFAULT zulassen.
Zesstra55f94fe2018-11-14 23:16:01 +0100271 return -1;
272 if (CannotSee(1)) {
273 notify_fail("Du kannst nichts sehen!\n", NF_NL_MAX);
274 return -1;
MG Mud User88f12472016-06-24 23:31:02 +0200275 }
276 sense = SENSE_READ;
277 }
Zesstra55f94fe2018-11-14 23:16:01 +0100278 // else ist normales Sehen/untersuchen
279 else
280 {
281 if (CannotSee(1)) {
282 notify_fail("Du kannst nichts sehen!\n", NF_NL_MAX);
283 return -1;
284 }
285 _notify_fail("Was willst Du denn untersuchen?\n");
286 if (!str) return -1;
287 sense = SENSE_VIEW;
MG Mud User88f12472016-06-24 23:31:02 +0200288 }
Zesstra55f94fe2018-11-14 23:16:01 +0100289 // "rieche an ..." etc.: das an rausschneiden, wird nicht gebraucht.
290 if (sizeof(str))
291 {
292 if (sense!=SENSE_VIEW)
293 {
294 string what;
295 if (sscanf(str,"an %s",what)==1) str=what;
296 }
297 }
MG Mud User88f12472016-06-24 23:31:02 +0200298
Zesstraa19328a2018-11-16 01:28:20 +0100299 switch(sense)
300 {
301 // Fuer diese Sinne muss der Container offen sein.
302 case SENSE_SMELL:
303 case SENSE_TOUCH:
304 case SENSE_SOUND:
305 transparent_check = function int (object o)
306 { return o->QueryProp(P_CNT_STATUS) == CNT_STATUS_OPEN;};
307 break;
308 // Fuer alle anderen reicht, wenn er transparent ist.
309 default:
310 transparent_check = function int (object o)
311 { return o->QueryProp(P_TRANSPARENT);};
312 }
313
Zesstra55f94fe2018-11-14 23:16:01 +0100314 return sense;
315}
316
Zesstraa19328a2018-11-16 01:28:20 +0100317private object get_ref_object(string *tokens, closure transparent_check,
318 int ref_given)
Zesstra55f94fe2018-11-14 23:16:01 +0100319{
320 object ref_object;
Zesstraa19328a2018-11-16 01:28:20 +0100321
322 // im Kommando speziell beschrieben ist, wo die Suche losgehen soll, spielt
323 // das Referenzobjekt keine Rolle.
324 if (sizeof(tokens) > 1 && tokens[<1] == "hier") {
325 tokens = tokens[..<2];
Zesstra55f94fe2018-11-14 23:16:01 +0100326 ref_object = environment();
Zesstraa19328a2018-11-16 01:28:20 +0100327 ref_given=1;
328 }
329 else if (sizeof(tokens) > 2 && tokens[<2] == "in")
330 {
331 if (tokens[<1] == "mir" ||
332 tokens[<1] == "dir")
333 {
334 tokens = tokens[..<3];
335 ref_object = this_object();
336 ref_given=1;
337 }
338 else if (tokens[<1] == "raum")
339 {
340 tokens = tokens[..<3];
341 ref_object = environment();
342 ref_given=1;
343 }
344 }
Zesstra55f94fe2018-11-14 23:16:01 +0100345 else
346 {
Zesstraa19328a2018-11-16 01:28:20 +0100347 // ansonsten bleibt tokens unmodifiziert und wir nehmen das
348 // Referenzobjekt (so existent).
349 ref_object = QueryProp(P_REFERENCE_OBJECT);
350 if (objectp(ref_object))
351 {
352 // Dieses muss aber "irgendwo" in unserem Umfeld sein.
353 if (ref_object == environment() || ref_object==this_object())
MG Mud User88f12472016-06-24 23:31:02 +0200354 {
Zesstraa19328a2018-11-16 01:28:20 +0100355 // Umgebung oder Spieler selber sind als Bezugsobjekt immer in
356 // Ordnung, nichts machen.
357 }
358 // Wenn es nicht wir oder environment ist, darf das Referenzobjekt
359 // nicht unsichtbar sein.
360 else if (!ref_object->short())
361 ref_object = 0;
362 // Und nur wenn das Ref-Objekt irgendwie in unserer Umgebung ist
363 // (direkt, in uns, in einem Container etc.) kommt es in Frage. Es
364 // wird daher geprueft, ob all_environments() des Ref-Objekts auch
365 // unser environment enthaelt.
366 else
367 {
Zesstrac67f5752018-12-03 20:09:28 +0100368 object *all_envs=all_environment(ref_object) || ({});
Zesstraa19328a2018-11-16 01:28:20 +0100369 int i=member(all_envs, environment());
370 if (i >= 0)
MG Mud User88f12472016-06-24 23:31:02 +0200371 {
Zesstraa19328a2018-11-16 01:28:20 +0100372 // dann von dem Environment an entlanglaufen und alle
373 // intermediaeren Objekt pruefen, ob sie einsehbar und sichtbar
374 // sind.
375 foreach(object env : all_envs[..i]) {
376 // Ist eine Umgebung Living oder intransparenter Container oder
377 // unsichtbar?
378 if (living(env) || !funcall(transparent_check, env)
379 || !env->short())
380 {
381 // Kette zum Ref-Objekt unterbrochen. Wenn das unterbrechende
382 // Env nicht dieses Living selber oder sein Env ist, wird das
383 // Referenzobjekt zusaetzlich genullt.
384 if (env != this_object() && env != environment())
385 ref_object = 0;
386 break;
MG Mud User88f12472016-06-24 23:31:02 +0200387 }
388 }
389 }
Zesstraa19328a2018-11-16 01:28:20 +0100390 else
391 ref_object = 0; // nicht im Raum oder Inventory
392 }
393 } // ref_object exists
MG Mud User88f12472016-06-24 23:31:02 +0200394 }
Zesstra55f94fe2018-11-14 23:16:01 +0100395 return ref_object;
396}
MG Mud User88f12472016-06-24 23:31:02 +0200397
Zesstraa19328a2018-11-16 01:28:20 +0100398// Versucht ein Basisobjekt (ggf. in env) finden, was durch <tokens>
399// beschrieben wird.
400// Das Basisobjekt ist das, was bei einem "detail an parent" durch parent
401// gekennzeichnet wird oder notfalls das Ref-Objekt selber. Zu beachten ist,
402// dass find_base() ggf. rekursiv arbeitet, um sowas wie "riss an kerze in
403// kerzenstaender auf tisch" zu finden. detail ist dann der letzte Teil der
404// tokens (der Anfang!), was keinen Container mehr bezeichnet, das gefundene
405// Objekt ist der letzte Container/Item auf dem Weg zu detail.
406// detail muss als Referenz uebergeben werden.
407// transparent_check ist die Closure, die prueft, ob man in einen
408// Container reinforschen (sehen, lesen, riechen, tasten etc.) kann. Sie
409// bekommt den Container uebergeben.
410private object find_base(object env, string *tokens, string detail,
411 closure transparent_check)
Zesstra55f94fe2018-11-14 23:16:01 +0100412{
Zesstraa19328a2018-11-16 01:28:20 +0100413 // zuerst pruefen, ob man in env reinsehen/reinforschen kann (in
414 // this_object() und environment() kann man immer reinsehen).
415 if (env && env != this_object() && env != environment())
MG Mud User88f12472016-06-24 23:31:02 +0200416 {
Zesstraa19328a2018-11-16 01:28:20 +0100417 // Nicht in Lebewesen reingucken und nicht in intransparente/geschlossene
418 // Container
419 if (living(env) || !funcall(transparent_check, env))
MG Mud User88f12472016-06-24 23:31:02 +0200420 {
Zesstraa19328a2018-11-16 01:28:20 +0100421 // suche nach Objekten in env ist jetzt zuende, weil nicht in env
422 // reinsehbar.
423 // Aber tokens koennte noch ein detail beschreiben, was der Aufrufer an
424 // env findet. Also tokens nach detail und env zurueckgeben.
425 detail = implode(tokens, " ");
426 return env;
MG Mud User88f12472016-06-24 23:31:02 +0200427 }
MG Mud User88f12472016-06-24 23:31:02 +0200428 }
Zesstraa19328a2018-11-16 01:28:20 +0100429 // ansonsten schauen, ob in tokens noch ein (weiterer) Container beschrieben
430 // wird.
431 for (int i = sizeof(tokens)-1; i > 1; i--)
Zesstra55f94fe2018-11-14 23:16:01 +0100432 {
Zesstradc0ef892018-11-18 23:36:03 +0100433 object ob, *objs;
Zesstraa19328a2018-11-16 01:28:20 +0100434 // In env oder mir oder umgebung gucken, ob ein passendes Objekt da ist.
435 // Hierfuer werden mit jedem Schleifendurchlauf von hinten mehr worte aus
436 // tokens benutzt. (Beispiel "muenze in boerse 2")
Zesstradc0ef892018-11-18 23:36:03 +0100437 // present waere schneller und die Komplexitaet von present_objects waere
438 // hier nicht noetig. Allerdings beruecksichtigt es die Sichtbarkeit und
439 // ggf. virtuell anwesende Objekte, was present nicht tut.
440 string suchid=implode(tokens[i..], " ");
Zesstraa19328a2018-11-16 01:28:20 +0100441 if (env)
Zesstradc0ef892018-11-18 23:36:03 +0100442 objs = env->present_objects(suchid) || ({});
Zesstraa19328a2018-11-16 01:28:20 +0100443 else
Zesstradc0ef892018-11-18 23:36:03 +0100444 objs = environment()->present_objects(suchid) ||
445 this_object()->present_objects(suchid);
446 // wenn nicht genau ein Objekt gefunden wird, wird weitergesucht, im
447 // naechsten Durchlauf mit einem Wort mehr aus tokens
Zesstraa81a17b2018-12-03 21:25:21 +0100448 if (!sizeof(objs))
Zesstradc0ef892018-11-18 23:36:03 +0100449 {
Zesstraa81a17b2018-12-03 21:25:21 +0100450 continue;
451 }
Zesstra9298c2d2018-12-03 21:32:59 +0100452 // falls mehr als ein Objekt gefunden wurden: das erste nehmen, wie
453 // ueberall sonst auch.
Zesstraa81a17b2018-12-03 21:25:21 +0100454 ob = objs[0];
Zesstraa19328a2018-11-16 01:28:20 +0100455
456 // an dieser Stelle wird (noch) nicht geprueft, ob man in das gefundene
457 // Objekt hineinforschen kann.
458
459 // Wenn passende Praeposition davor (z.B. "in" (beutel), "auf" (tisch)),
460 // rekursiv weitersuchen, ob der restliche Teil von tokens evtl. nochmal
461 // nen container in dem gefundenen Objekt bezeichnet.
462 if (tokens[i-1] == ob->QueryProp(P_PREPOSITION))
463 return find_base(ob, tokens[..i-2], &detail, transparent_check);
464
465 // Wenn tokens[i-1] "an" oder "am" ist, wird zwar nicht weiter verzweigt,
466 // aber vermutlich wird ein Detail an diesem Objekt gesucht, d.h. das
467 // Objekt wird env, tokens entsprechend gekuerzt und die Schleife beendet.
468 // Bemerkung: haette ob tatsaechlich eine Praeposition an|am gehabt,
469 // waeren wir vorher in find_base() verzweigt zum Suchen in dem Container.
470 if (tokens[i-1] == "an" || tokens[i-1] == "am") {
471 env = ob;
472 tokens=tokens[..i-2];
473 break;
474 }
Zesstra55f94fe2018-11-14 23:16:01 +0100475 }
Zesstraa19328a2018-11-16 01:28:20 +0100476 // an diesem Punkt sind in tokens nur noch worte drin, die keinen Container
477 // mehr beschreiben. Die Resttokens kommen nach <detail>, sie beschreiben
478 // dann entweder ein Objekt oder ein Detail.
479 detail = implode(tokens, " ");
480
481 // Wenn es ein env gibt, beschreibt detail nun ein Objekt in env oder ein
482 // Detail an env, was der Aufrufer prueft.
483 // Wenn es aber kein env gibt (das passiert, wenn der Aufrufer dieser
484 // Funktion schon kein env (->Referenzobjekt) hereingegeben hat, wurde auch
485 // kein Container gefunden, der durch die tokens beschrieben wurde. Dann
486 // kann der Aufrufer noch an/in this_object() oder environment() gucken, ob
487 // was zu finden ist.
488 return env;
Zesstra55f94fe2018-11-14 23:16:01 +0100489}
MG Mud User88f12472016-06-24 23:31:02 +0200490
Zesstra55f94fe2018-11-14 23:16:01 +0100491private nosave int exa_cnt;
492private nosave int exa_time;
493private nosave string *exa;
494private void unt_script_dings(string str)
495{
496 // unt-script-sucher
497 if (!sizeof(str)) return;
498 if (member(({"boden","decke","wand","waende"}),old_explode(str," ")[0]) == -1) {
499 exa_cnt -= (time() - exa_time)/2;
500 exa_time = time();
501 exa_cnt++;
502 if (!exa)
503 exa = ({ str });
504 else
505 exa += ({ str });
506 if (exa_cnt > 10) {
507 log_file("ARCH/LOOK",
508 sprintf("%s: %s in %s\n%@O\n",dtime(time()),getuid(this_object()),
509 environment() ? object_name(environment()) : "???",exa), 150000);
510 exa_cnt = 0;
511 exa = ({});
512 }
513 else if (exa_cnt < 0) {
514 exa_cnt = 0;
515 exa = ({});
516 }
517 }
518}
519
Zesstrafc501e12018-12-02 23:37:33 +0100520private int _examine_rec(object *ref_objects, string *tokens, closure
Zesstraa19328a2018-11-16 01:28:20 +0100521 transparent_check, int sense, int mode,
522 int ref_given)
523{
Zesstrafc501e12018-12-02 23:37:33 +0100524 // ref_objects sind alle in Frage kommenden Kandidaten, an denen wir suchen.
525
Zesstraa19328a2018-11-16 01:28:20 +0100526 // Sodann ein Basisobjekt finden. Das Basisobjekt ist das, was bei einem
527 // "detail an parent" durch parent gekennzeichnet wird oder notfalls das
528 // Ref-Objekt von oben. Zu beachten ist, dass find_base() ggf. rekursiv
529 // arbeitet, um sowas wie "riss an kerze in kerzenstaender auf tisch" zu
530 // finden. detail ist dann das letzte Teil der tokens, der keinen Container
531 // mehr bezeichnet, das gefundene Objekt ist der letzte Container/Item auf
532 // dem Weg zu detail. (detail==riss, base==kerze)
533 string detail; // detail an parent
534 // das fuer parent gefundene objekt
Zesstrafc501e12018-12-02 23:37:33 +0100535 object base = find_base(ref_objects[0], tokens, &detail, transparent_check);
536 // BTW: find_base liefert immer was zurueck - wenn nix gefunden wird, das
537 // ref_object, was als Start der Suche uebergeben wurde.
Zesstraa19328a2018-11-16 01:28:20 +0100538 // Jetzt wird versucht, ein Objekt mit der ID "detail" in base oder
Zesstrafc501e12018-12-02 23:37:33 +0100539 // ein Detail an base zu finden.
540
Zesstraa19328a2018-11-16 01:28:20 +0100541 object *objs;
Zesstrafc501e12018-12-02 23:37:33 +0100542 // Wenn wir in base reingucken koennen, ermitteln wir alle zu
Zesstraa19328a2018-11-16 01:28:20 +0100543 // detail in Frage kommenanden Objekte in seinem Inventar
Zesstrafc501e12018-12-02 23:37:33 +0100544 if (base == this_object() || base == environment() ||
545 (funcall(transparent_check, base) && !living(base)))
Zesstraa19328a2018-11-16 01:28:20 +0100546 {
Zesstrafc501e12018-12-02 23:37:33 +0100547 objs = base->locate_objects(detail, 1) || ({});
Zesstraa19328a2018-11-16 01:28:20 +0100548 }
Zesstraa19328a2018-11-16 01:28:20 +0100549 else
550 {
Zesstrafc501e12018-12-02 23:37:33 +0100551 // Basisobjekt da, aber nicht reinguckbar, also keine Objekte
552 // gefunden. base aber nicht nullen, denn es ist ja noch gueltig fuer
553 // Detailsuchen an base...
554 objs = ({});
Zesstraa19328a2018-11-16 01:28:20 +0100555 }
556 // Und in jedem Fall werden alle Objekt raussortiert, die unsichtbar sind.
557 // ;-)
558 objs = filter_objects(objs, "short"); // nur sichtbare...
559
560 // Wenn es sichtbare gibt, werden die ggf. angeguckt.
561 if (sizeof(objs))
562 {
563 // Objekte gefunden, mal schauen, ob die taugen (d.h. fuer den jew. Sinn
564 // Infos haben. Wenn nicht, muessen wir weitersuchen.
565 // Aber erstmal die Objekte durchlaufen.
566 foreach(object ob: objs)
567 {
568 string out;
569 if (sense == SENSE_VIEW)
570 {
571 out = ob->long(mode);
572 }
573 else if (sense == SENSE_READ)
574 {
575 // Extrawurst: P_READ_MSG auch noch abfragen.
576 out = ob->QueryProp(P_READ_MSG);
577 if (!stringp(out))
578 out = ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),SENSE_READ);
579 }
580 else
581 out=ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),sense);
582 // Wenn was gefunden wurde, sind wir fertig.
583 if (stringp(out))
584 {
585 SetProp(P_REFERENCE_OBJECT, ob);
586 tell_object(ME, out);
587 return 1;
588 }
589 }
590 }
591 // keine Objekte gefunden oder die hatten nix fuer unseren Sinn
592 // dabei. Also nach ordinaeren Details an base/parent suchen.
593 string out = base->GetDetail(detail, QueryProp(P_REAL_RACE), sense);
594 // Achja, Tueren gibt es ja auch noch. :-o (TODO: die solten vItems werden)
595 if (!out && sense==SENSE_VIEW)
596 out = base->GetDoorDesc(detail);
597 if (out)
598 {
599 // Detail gefunden, base darf neues Referenzobjekt werden und nach
600 // Ausgabe sind wir fertig.
601 SetProp(P_REFERENCE_OBJECT, base);
602 write(out);
603 return 1;
604 }
605 else
606 {
607 // wenn auch keine Details gefunden, dann schauen, unser Env evtl.
608 // noch nen passendes Detail oder Objekt hat. Das ist aber natuerlich
609 // nur der Fall, wenn ref_object nicht eh schon das Environment war.
610 // Ausserdem nicht machen, wenn der Nutzer ein spezifisches Referenzobjekt
611 // angegeben hat (z.B. "in raum").
Zesstrafc501e12018-12-02 23:37:33 +0100612 if (sizeof(ref_objects) > 1)
Zesstraa19328a2018-11-16 01:28:20 +0100613 {
Zesstrafc501e12018-12-02 23:37:33 +0100614 // Wir schauen uns ggf. weitere Kandidaten an. Bei den anderen
Zesstraa19328a2018-11-16 01:28:20 +0100615 // Sinnen kein neues notify_fail, weil sonst staendig Meldungen kommen,
616 // dass x nicht da ist, nur weil es keine Beschreibung fuer den Sinn hat.
617 if (sense==SENSE_VIEW)
Zesstrafc501e12018-12-02 23:37:33 +0100618 {
Zesstra9a083752018-12-05 20:18:23 +0100619 // Ausgabe, was gerade angeguckt hat.
620 if (ref_objects[0] == environment())
621 out="Sowas siehst Du hier nicht. ";
622 else if (ref_objects[0] == this_object())
623 out="Sowas siehst Du an Dir nicht. ";
624 else
625 out="Sowas siehst Du an/in "+ref_objects[0]->name(WEM) +" nicht. ";
626 // Ausgabe, was man als naechstes anguckt.
627 if (ref_objects[1] == environment())
628 out+="Du schaust Dich um.";
629 else
630 out+="Du schaust Dich an.";
631
632 write(break_string(out,78));
Zesstrafc501e12018-12-02 23:37:33 +0100633 }
634 return _examine_rec(ref_objects[1..], tokens, transparent_check,
635 sense, mode, 0);
Zesstraa19328a2018-11-16 01:28:20 +0100636 }
637 // Leider ist nix mehr uebrig zum angucken und in jedem Fall Ende.
638 else
639 {
640 // Wenn spezifisches ref_object vom Nutzer angegeben (ref_given!=0),
641 // gibt es eine leicht andere Meldung und das Bezugsobjekt muss weg.
Zesstraa19328a2018-11-16 01:28:20 +0100642 if (ref_given) {
643 SetProp(P_REFERENCE_OBJECT, 0);
Zesstra9ed8c9b2018-12-03 21:23:56 +0100644 notify_fail("\'" + capitalize(detail) + "\'"
645 + (sense==SENSE_VIEW ? " siehst " : " findest ")
646 + "Du da nicht!\n");
Zesstraa19328a2018-11-16 01:28:20 +0100647 }
Zesstra9ed8c9b2018-12-03 21:23:56 +0100648 // Ansonsten gibt es nur nen passendes notify_fail und Ende.
Zesstraa19328a2018-11-16 01:28:20 +0100649 else if (sense==SENSE_VIEW) {
Zesstra9ed8c9b2018-12-03 21:23:56 +0100650 notify_fail("\'" + capitalize(detail) + "\'"
651 + " siehst Du auch da nicht!\n");
652 }
653 else {
654 notify_fail("\'" + capitalize(detail) + "\'"
655 + " findest Du da nicht!\n");
Zesstraa19328a2018-11-16 01:28:20 +0100656 }
657 return 0;
658 }
659 }
660 // Nie erreicht.
661 return 0;
662}
663
Zesstra55f94fe2018-11-14 23:16:01 +0100664varargs int _examine(string str, int mode)
665{
666 // Sinn auswaehlen, der benutzt wird. Wenn -1, abbrechen (kein Sinn bzw.
667 // Sinn erlaubt ggf. kein leeren <str> fuer Defaulmeldung). Je nach Sinn
Zesstraa19328a2018-11-16 01:28:20 +0100668 // wird auch <str> modifiziert.
669 // transparent_check enthaelt spaeter die Closure, die entscheidet,
670 // ob man mit dem Sinn in einen Container reinforschen kann.
671 closure transparent_check;
672 int sense=select_sense(&str, &transparent_check);
Zesstra55f94fe2018-11-14 23:16:01 +0100673 if (sense<0) return 0;
674
675 unt_script_dings(str);
676
677 // Wenn kein str, dann SENSE_DEFAULT vom Environment ausgeben. Bei
Zesstraa19328a2018-11-16 01:28:20 +0100678 // SENSE_VIEW wird das bereits bei den Aufrufern dieser Funktion gemacht und
679 // die Beschreibung der Umgebung ausgegeben.
Zesstra55f94fe2018-11-14 23:16:01 +0100680 if (sense!=SENSE_VIEW)
681 {
682 if (!str)
683 {
684 string detail = environment()->GetDetail(SENSE_DEFAULT,
685 QueryProp(P_REAL_RACE),sense);
686 if(!detail)
687 return 0;
688 write(detail);
689 return 1;
690 }
691 }
692
Zesstraa19328a2018-11-16 01:28:20 +0100693 string *tokens = explode(str, " ");
694 int ref_given;
695 // Das Ref-Objekt finden, das kann dann auch in tokens angegeben sein. In dem
696 // Fall wird die Liste an tokens um die Angabe des Ref-Objekts gekuerzt und
697 // ref_given gesetzt.
Zesstrafc501e12018-12-02 23:37:33 +0100698 object ref_ob = get_ref_object(&tokens, transparent_check, &ref_given);
Zesstraa19328a2018-11-16 01:28:20 +0100699 // Bemerkung: Das Referenzobjekt ist garantiert sichtbar, aber nicht
700 // unbedingt einsehbar. Wird weiter unten geprueft.
Zesstra55f94fe2018-11-14 23:16:01 +0100701
Zesstrafc501e12018-12-02 23:37:33 +0100702 // Falls nicht explizit ein Objekt als Ref-Objekt angegeben wurde, werden
703 // max. 3 Objekte angeguckt: das eigentliche Referenzobjekt, das
704 // Environment und das eigene Objekt. Da ref_object auch eines der letzten
705 // beiden sein kann, muss die Dublette ggf. raus.
706 object *ref_objects;
707 if (ref_ob)
708 {
709 ref_objects=({ref_ob});
710 if (!ref_given)
711 ref_objects += ({environment(), this_object()}) - ref_objects;
712
713 }
714 else
715 ref_objects = ({environment(), this_object()});
716
717 return _examine_rec(ref_objects, tokens, transparent_check, sense, mode,
718 ref_given);
MG Mud User88f12472016-06-24 23:31:02 +0200719}
720
Zesstra55f94fe2018-11-14 23:16:01 +0100721// Funktion fuer "schau in ..."
MG Mud User88f12472016-06-24 23:31:02 +0200722varargs int look_into(string str,int mode)
723{
724 object *found_obs;
725
726 if( CannotSee() ) return 1;
Zesstra55f94fe2018-11-14 23:16:01 +0100727 _notify_fail("Wo willst Du denn reinschauen?\n");
728 found_obs=find_objects(str, 0, 0);
729 if (!sizeof(found_obs))
MG Mud User88f12472016-06-24 23:31:02 +0200730 {
731 if (environment() &&
732 (environment()->GetDetail(str,QueryProp(P_REAL_RACE))||
733 environment()->GetDoorDesc(str)))
734 _notify_fail("Da kannst Du so nicht reinsehen.\n");
735 return 0;
736 }
Zesstra55f94fe2018-11-14 23:16:01 +0100737
MG Mud User88f12472016-06-24 23:31:02 +0200738 return _examine(str, mode);
739}
740
741/* Gebe die Umgebung des aktiven Spielers zurueck, lasse dabei */
742/* rekursiv geschachtelte Raeume zu. */
743/* Wenn allow_short 0 ist, so wird immer die long-descr benutzt */
744varargs string env_descr(int allow_short,int flags, int force_short )
745{
746 object env;
747 int brief;
748
749 env = environment(ME);
750
751 if(!env)
752 return "Du schwebst im Nichts ... Du siehst nichts, rein gar nichts ...\n";
753
754 if (!force_short && (!allow_short || !QueryProp(P_BRIEF)))
755 return env->int_long(ME,ME,flags);
756
757 if (!flags && ((brief=QueryProp(P_BRIEF))>=2))
758 return "";
759
760 return env->int_short(ME,ME);
761}
762
Zesstra55f94fe2018-11-14 23:16:01 +0100763// Kommandofunktion fuer schau.
764// Verzweigt ggf. in _examine() fuers normale Untersuchen.
MG Mud User88f12472016-06-24 23:31:02 +0200765int _look(string str)
766{
767 string s;
768 int flag;
769
770 if(CannotSee()) return 1;
771
Zesstra55f94fe2018-11-14 23:16:01 +0100772 // nur schau mit ggf. Flags entsorgt das Ref-Objekt und schaut die
773 // Raumbeschreibung an.
MG Mud User88f12472016-06-24 23:31:02 +0200774 if(!str)
775 {
776 SetProp(P_REFERENCE_OBJECT, 0);
777 write( env_descr() );
778 return 1;
779 }
780 if(str=="-f" || str=="genau")
781 {
782 SetProp(P_REFERENCE_OBJECT, 0);
783 write( env_descr(0,2) );
784 return 1;
785 }
786 if(str=="-k" || str=="kurz")
787 {
788 SetProp(P_REFERENCE_OBJECT, 0);
789 write( env_descr(1,2,1) );
790 return 1;
791 }
Zesstra55f94fe2018-11-14 23:16:01 +0100792 // Ansonsten wird in _examine() weitergemacht, wobei Flags vorher
793 // rausgeschnitten werden. _examine() benutzt die Flags, falls es eine
794 // Langbeschreibung eines Objektes ausgeben wird.
MG Mud User88f12472016-06-24 23:31:02 +0200795 if(str[0..2]=="-f "){
796 flag=2;
797 str=str[3..];
798 }
799 else if(str[0..5]=="genau "){
800 flag=2;
801 str=str[6..];
802 }
803 else flag = 0;
Zesstra55f94fe2018-11-14 23:16:01 +0100804
MG Mud User88f12472016-06-24 23:31:02 +0200805 if (sscanf(str,"%s an",s)) str=s;
Zesstra55f94fe2018-11-14 23:16:01 +0100806 // "in mir", "in dir" soll in _examine rein, aber "in ..." in look_into().
MG Mud User88f12472016-06-24 23:31:02 +0200807 if (sscanf(str,"%s in mir",s)||sscanf(str,"%s in dir",s)) return _examine(str,flag);
808 if (sscanf(str,"in %s",s)) return look_into(s,flag);
Zesstra55f94fe2018-11-14 23:16:01 +0100809 // Alles andere weiter an _examine().
MG Mud User88f12472016-06-24 23:31:02 +0200810 return _examine(str,flag);
811}
812
813int _equipment(string arg)
814{
815 if (CannotSee()) return 1;
816 call_other("/std/player/invmaster/invmaster", "ShowInv", ME, arg);
817 return 1;
818}
819
820static mixed _query_localcmds()
821{
822 return
823 ({({"ausruestung", "_equipment",0,0}),
824 ({"i","_inventory",0,0}),
825 ({"inv","_inventory",0,0}),
826 ({"inventur","_inventory",0,0}),
827 ({"schau","_look",0,0}),
828 ({"schaue","_look",0,0}),
829 ({"unt","_examine",0,0}),
830 ({"untersuch","_examine",0,0}),
831 ({"betracht","_examine",0,0}),
832 ({"untersuche","_examine",0,0}),
833 ({"betrachte","_examine",0,0}),
834 ({"betr","_examine",0,0}),
Zesstra55f94fe2018-11-14 23:16:01 +0100835 ({"lausche","_examine",0,0}),
836 ({"lausch","_examine",0,0}),
837 ({"hoer","_examine",0,0}),
838 ({"hoere","_examine",0,0}),
839 ({"lies","_examine",0,0}),
840 ({"lese","_examine",0,0}),
841 ({"les","_examine",0,0}),
842 ({"schnupper","_examine",0,0}),
843 ({"schnuppere","_examine",0,0}),
844 ({"riech","_examine",0,0}),
845 ({"rieche","_examine",0,0}),
846 ({"taste","_examine",0,0}),
847 ({"beruehre","_examine",0,0}),
848 ({"beruehr","_examine",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +0200849 ({"kurz","_toggle_brief",0,0}),
850 ({"lang","_toggle_brief",0,0}),
851 ({"ultrakurz","_toggle_brief",0,0})
852 });
853}