blob: 3b5b98239d278650af941b1c515ac12dd8bfd627 [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
Zesstra55f94fe2018-11-14 23:16:01 +010023#define NEED_PROTOTYPES
24#include <container.h>
25#include <thing/properties.h>
26#include <living/put_and_get.h>
27#include <living/description.h>
28#include <living/put_and_get.h>
29#include <player/command.h>
30
Zesstrabd66df72019-04-04 20:53:43 +020031//#define DEBUG(x, y, z) send_debug(x, y, z)
32#define DEBUG(x, y, z)
33
MG Mud User88f12472016-06-24 23:31:02 +020034varargs mixed More(string str, int fflag, string returnto);
35
36void create()
37{
38 Set(P_BRIEF, SAVE, F_MODE);
39 Set(P_BLIND, SAVE, F_MODE);
40}
41
42int _toggle_brief()
43{
44 int brief;
45
46 if (query_verb()=="kurz")
47 brief=1;
48 else if (query_verb()=="ultrakurz")
49 brief=2;
50 else brief=0;
51 SetProp(P_BRIEF, brief);
52 write("Du bist nun im \""+
53 (brief?(brief==1?"Kurz":"Ultrakurz"):"Lang")+"\"modus.\n");
54 return 1;
55}
56
57private int sortinv(mixed a, mixed b) { return a[0] > b[0]; }
58
59private string collectinv(mixed obj)
60{
61 if(obj[0]=="") return 0;
62 return (obj[2] ? " " : "")
63 + obj[0]
64 + (obj[1] > 1 ? " ("+obj[1]+")" : "");
65}
66
67#define I_AUTOLOAD 1
68#define I_KEEP 4
69#define I_FORMATTED 16
70#define I_ARMOUR 64
71#define I_SORT 256
72#define I_WEAPON 1024
73#define I_FORCE_SORT 4096
74#define I_NO_TABLE 16384
75
76private string getflags(string arg, int flags)
77{
78 int no, i;
79 if(sizeof(arg) < 2) return 0;
80 no = (arg[0] == '-');
81
82 for(i = 1; i < sizeof(arg); i++)
83 {
84 switch(arg[i])
85 {
86 case 'a': flags |= I_AUTOLOAD << no; break;
87 case 'b': flags |= I_KEEP << no; break;
88 case 'f': flags |= I_FORMATTED << no; break;
89 case 'r': flags |= I_ARMOUR << no; break;
90 case 's': flags |= I_SORT << no; break;
91 case 'w': flags |= I_WEAPON << no; break;
92 case 'v': flags |= (I_ARMOUR | I_WEAPON) << !no; break;
93 case '1': flags |= I_NO_TABLE; break;
94 // Die Option macht nur Aerger und kommentiert ist sie eh nicht.
95 // Wer das dringend braucht, soll Wargons Schiebepuzzle benutzen.
96 //
97 // Tiamak, 15.10.2000
98 // case 'S': flags |= I_FORCE_SORT << no; break;
99 default : return arg[i..i]; // wird ausgegeben an Spieler als unbekannt.
100 }
101 }
102 return 0;
103}
104
105static int _check_keep(object ob)
106{
107 return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
108}
109
110int _inventory(string str)
111{
112 mixed *args, output;
113 int ansi, i, flags, minv;
114 mixed inventory, weapons, armours, misc;
115 string format;
116
117 if(CannotSee()) return 1;
118
119 if((str = _unparsed_args()) && str!="")
120 {
121 string error;
122 error = "Benutzung: i[nventar] [-/+1abfrsvw]\n";
123 args = regexp(regexplode(str, "[-+][1abfrswv][1abfrswv]*"),
124 "[-+][1abfrswv][1abfrswv]*");
125 if(!sizeof(args)) return (_notify_fail(error), 0);
126 if(sizeof(args = map(args, #'getflags/*'*/, &flags) - ({ 0 })))
127 {
128 printf("%s: Unbekanntes Argument.\n"+error, implode(args, ", "));
129 return 1;
130 }
131 }
132 // Fuer Spieler gehen nur sichtbare Objekte in den Algorithmus
133 if (IS_LEARNING(ME))
134 inventory = all_inventory(ME);
135 else
136 inventory = filter_objects(all_inventory(ME), "short");
137
138 ansi = member(({"vt100", "ansi"}), QueryProp(P_TTY)) != -1;
139 minv = 1 | (flags & (I_FORMATTED | (I_FORMATTED << 1)) ? 2 : 0);
140 format = (flags & I_NO_TABLE) ? "=" : "#";
141
142// if(flags & (I_FORCE_SORT | I_FORCE_SORT << 1))
143// {
144// closure sf;
145// sf = flags & I_FORCE_SORT ? #'> : #'<;
146// s = sort_array(s, lambda(({'a, 'b}),
147// ({#'funcall, sf,
148// ({#'||,({#'call_other,'a,"short"}),""}),
149// ({#'||,({#'call_other,'b,"short"}),""})})));
150// map_objects(s, "move", this_object());
151// s = all_inventory(ME);
152// }
153
154 if (flags & I_AUTOLOAD)
155 inventory = filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
156 else if (flags & (I_AUTOLOAD << 1))
157 inventory -= filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
158
159 if(flags & I_KEEP)
160 inventory = filter(inventory,#'_check_keep);
161 else if(flags & (I_KEEP << 1))
162 inventory -= filter(inventory,#'_check_keep);
163
164 armours = filter_objects(inventory, "QueryProp", P_ARMOUR_TYPE);
165 // Kleidung dazu addieren, vorher die erkannten Ruestungen abziehen, die
166 // muessen nicht nochmal durchiteriert werden.
167 armours += filter_objects(inventory-armours, "IsClothing");
168 // Ruestungen werden hier nicht abgezogen, weil es Kram gibt, welche sowohl
169 // Ruestung als auch Waffe ist.
170 weapons = filter_objects(inventory, "QueryProp", P_WEAPON_TYPE);
171 misc = inventory - weapons - armours; // rest ;-)
172
173 if(flags & I_WEAPON)
174 {
175 inventory = weapons; misc = ({});
176 if(!(flags & (I_ARMOUR))) armours = ({});
177 }
178 if(flags & I_ARMOUR)
179 {
180 inventory = armours; misc = ({});
181 if(!(flags & I_WEAPON)) weapons = ({});
182 }
183 if(flags & (I_WEAPON << 1)) { weapons = ({}); inventory = armours + misc; }
184 if(flags & (I_ARMOUR << 1)) { armours = ({}); inventory = weapons + misc; }
185
186 output = "";
187 if(flags & (I_FORMATTED | (I_FORMATTED << 1)))
188 {
189 inventory = make_invlist(this_player(), inventory, minv);
190 if(flags & (I_SORT | (I_SORT << 1)))
191 inventory = sort_array(inventory, #'sortinv/*'*/);
192 output += sprintf("%"+format+"-78s\n",
193 implode(map(inventory,#'collectinv/*'*/),"\n"));
194 }
195 else
196 {
197 if(weapons && sizeof(weapons))
198 {
199 weapons = make_invlist(this_player(), weapons, minv);
200 if(flags & (I_SORT | (I_SORT << 1)))
201 weapons = sort_array(weapons, #'sortinv/*'*/);
202 output += (ansi?ANSI_BOLD:"") + "Waffen:" + (ansi?ANSI_NORMAL:"")+"\n"
203 + sprintf("%"+format+"-78s\n",
204 implode(map(weapons, #'collectinv/*'*/), "\n"));
205 }
206 if(armours && sizeof(armours))
207 {
208 armours = make_invlist(this_player(), armours, minv);
209 if(flags & (I_SORT | (I_SORT << 1)))
210 armours = sort_array(armours, #'sortinv/*'*/);
211 output += (ansi?ANSI_BOLD:"")
212 + "Kleidung & Ruestungen:" + (ansi?ANSI_NORMAL:"")+"\n"
213 + sprintf("%"+format+"-78s\n",
214 implode(map(armours, #'collectinv/*'*/), "\n"));
215 }
216 if(misc && sizeof(misc))
217 {
218 misc = make_invlist(this_player(), misc, minv);
219 if(flags & (I_SORT | (I_SORT << 1)))
220 misc = sort_array(misc, #'sortinv/*'*/);
221 output += (ansi?ANSI_BOLD:"") + "Verschiedenes:" + (ansi?ANSI_NORMAL:"")+"\n"
222 + sprintf("%"+format+"-78s\n",
223 implode(map(misc, #'collectinv/*'*/), "\n"));
224 }
225 }
226
227 // Spielerwunsch: 'inventar' sollte doch das Bezugsobjekt auf den Spieler
228 // aendern.
229 SetProp(P_REFERENCE_OBJECT, this_object());
230
231 if (output=="")
232 output += (ansi?ANSI_BOLD:"")+"Die Liste ist leer."+(ansi?ANSI_NORMAL:"");
233 More(output);
234 return 1;
235}
236
Zesstraa19328a2018-11-16 01:28:20 +0100237private int select_sense(string str, closure transparent_check)
MG Mud User88f12472016-06-24 23:31:02 +0200238{
Zesstra55f94fe2018-11-14 23:16:01 +0100239 int sense = -1;
MG Mud User88f12472016-06-24 23:31:02 +0200240 if(member(({"riech","rieche","schnupper","schnuppere"}),query_verb())!=-1)
241 {
242 _notify_fail("Du kannst nichts Besonderes riechen.\n");
243 sense = SENSE_SMELL;
244 }
245 else if(member(({"lausche","lausch","hoer","hoere"}),query_verb())!=-1)
246 {
247 if(QueryProp(P_DEAF))
Zesstra55f94fe2018-11-14 23:16:01 +0100248 {
249 notify_fail("Du bist taub!\n");
250 return -1;
251 }
MG Mud User88f12472016-06-24 23:31:02 +0200252 _notify_fail("Du kannst nichts Besonderes hoeren.\n");
253 sense = SENSE_SOUND;
254 }
255 else if(member(({"taste","beruehre","beruehr"}),query_verb())!=-1)
256 {
257 sense = SENSE_TOUCH;
258 // ein "ab" ganz am Ende von str wird abgeschnitten, es soll sowohl "taste
259 // x ab" als auch "taste x" funktionieren.
260 if (str) {
261 _notify_fail("Sowas kannst Du hier nicht ertasten!\n");
262 string *tmp = explode(str," ");
263 if (sizeof(tmp) > 1 && tmp[<1] == "ab")
264 str = implode(tmp[0..<2], " ");
265 }
266 else
267 _notify_fail("Was willst Du denn abtasten?\n");
268 }
269 else if (member(({"lies","lese","les"}), query_verb()) > -1)
270 {
271 _notify_fail("Was willst Du lesen?\n");
272 if ( !str ) // Kein SENSE_DEFAULT zulassen.
Zesstra55f94fe2018-11-14 23:16:01 +0100273 return -1;
274 if (CannotSee(1)) {
275 notify_fail("Du kannst nichts sehen!\n", NF_NL_MAX);
276 return -1;
MG Mud User88f12472016-06-24 23:31:02 +0200277 }
278 sense = SENSE_READ;
279 }
Zesstra55f94fe2018-11-14 23:16:01 +0100280 // else ist normales Sehen/untersuchen
281 else
282 {
283 if (CannotSee(1)) {
284 notify_fail("Du kannst nichts sehen!\n", NF_NL_MAX);
285 return -1;
286 }
287 _notify_fail("Was willst Du denn untersuchen?\n");
288 if (!str) return -1;
289 sense = SENSE_VIEW;
MG Mud User88f12472016-06-24 23:31:02 +0200290 }
Zesstra55f94fe2018-11-14 23:16:01 +0100291 // "rieche an ..." etc.: das an rausschneiden, wird nicht gebraucht.
292 if (sizeof(str))
293 {
294 if (sense!=SENSE_VIEW)
295 {
296 string what;
297 if (sscanf(str,"an %s",what)==1) str=what;
298 }
299 }
MG Mud User88f12472016-06-24 23:31:02 +0200300
Zesstraa19328a2018-11-16 01:28:20 +0100301 switch(sense)
302 {
303 // Fuer diese Sinne muss der Container offen sein.
304 case SENSE_SMELL:
305 case SENSE_TOUCH:
306 case SENSE_SOUND:
307 transparent_check = function int (object o)
308 { return o->QueryProp(P_CNT_STATUS) == CNT_STATUS_OPEN;};
309 break;
310 // Fuer alle anderen reicht, wenn er transparent ist.
311 default:
312 transparent_check = function int (object o)
Arathornc08a1e52019-01-21 18:56:32 +0100313 { return (o->QueryProp(P_TRANSPARENT) != 0); };
Zesstraa19328a2018-11-16 01:28:20 +0100314 }
315
Zesstra55f94fe2018-11-14 23:16:01 +0100316 return sense;
317}
318
Zesstraa19328a2018-11-16 01:28:20 +0100319private object get_ref_object(string *tokens, closure transparent_check,
320 int ref_given)
Zesstra55f94fe2018-11-14 23:16:01 +0100321{
322 object ref_object;
Zesstraa19328a2018-11-16 01:28:20 +0100323
324 // im Kommando speziell beschrieben ist, wo die Suche losgehen soll, spielt
325 // das Referenzobjekt keine Rolle.
326 if (sizeof(tokens) > 1 && tokens[<1] == "hier") {
327 tokens = tokens[..<2];
Zesstra55f94fe2018-11-14 23:16:01 +0100328 ref_object = environment();
Zesstraa19328a2018-11-16 01:28:20 +0100329 ref_given=1;
330 }
331 else if (sizeof(tokens) > 2 && tokens[<2] == "in")
332 {
333 if (tokens[<1] == "mir" ||
334 tokens[<1] == "dir")
335 {
336 tokens = tokens[..<3];
337 ref_object = this_object();
338 ref_given=1;
339 }
340 else if (tokens[<1] == "raum")
341 {
342 tokens = tokens[..<3];
343 ref_object = environment();
344 ref_given=1;
345 }
346 }
Zesstra55f94fe2018-11-14 23:16:01 +0100347 else
348 {
Zesstraa19328a2018-11-16 01:28:20 +0100349 // ansonsten bleibt tokens unmodifiziert und wir nehmen das
350 // Referenzobjekt (so existent).
351 ref_object = QueryProp(P_REFERENCE_OBJECT);
352 if (objectp(ref_object))
353 {
354 // Dieses muss aber "irgendwo" in unserem Umfeld sein.
355 if (ref_object == environment() || ref_object==this_object())
MG Mud User88f12472016-06-24 23:31:02 +0200356 {
Zesstraa19328a2018-11-16 01:28:20 +0100357 // Umgebung oder Spieler selber sind als Bezugsobjekt immer in
358 // Ordnung, nichts machen.
359 }
360 // Wenn es nicht wir oder environment ist, darf das Referenzobjekt
361 // nicht unsichtbar sein.
362 else if (!ref_object->short())
363 ref_object = 0;
364 // Und nur wenn das Ref-Objekt irgendwie in unserer Umgebung ist
365 // (direkt, in uns, in einem Container etc.) kommt es in Frage. Es
366 // wird daher geprueft, ob all_environments() des Ref-Objekts auch
367 // unser environment enthaelt.
368 else
369 {
Zesstrac67f5752018-12-03 20:09:28 +0100370 object *all_envs=all_environment(ref_object) || ({});
Zesstraa19328a2018-11-16 01:28:20 +0100371 int i=member(all_envs, environment());
372 if (i >= 0)
MG Mud User88f12472016-06-24 23:31:02 +0200373 {
Zesstraa19328a2018-11-16 01:28:20 +0100374 // dann von dem Environment an entlanglaufen und alle
375 // intermediaeren Objekt pruefen, ob sie einsehbar und sichtbar
376 // sind.
377 foreach(object env : all_envs[..i]) {
378 // Ist eine Umgebung Living oder intransparenter Container oder
379 // unsichtbar?
380 if (living(env) || !funcall(transparent_check, env)
381 || !env->short())
382 {
383 // Kette zum Ref-Objekt unterbrochen. Wenn das unterbrechende
384 // Env nicht dieses Living selber oder sein Env ist, wird das
385 // Referenzobjekt zusaetzlich genullt.
386 if (env != this_object() && env != environment())
387 ref_object = 0;
388 break;
MG Mud User88f12472016-06-24 23:31:02 +0200389 }
390 }
391 }
Zesstraa19328a2018-11-16 01:28:20 +0100392 else
393 ref_object = 0; // nicht im Raum oder Inventory
394 }
395 } // ref_object exists
MG Mud User88f12472016-06-24 23:31:02 +0200396 }
Zesstrabd66df72019-04-04 20:53:43 +0200397 DEBUG(PL, sprintf("%O\n",ref_object), "get_ref_object:");
Zesstra55f94fe2018-11-14 23:16:01 +0100398 return ref_object;
399}
MG Mud User88f12472016-06-24 23:31:02 +0200400
Zesstraa19328a2018-11-16 01:28:20 +0100401// Versucht ein Basisobjekt (ggf. in env) finden, was durch <tokens>
402// beschrieben wird.
403// Das Basisobjekt ist das, was bei einem "detail an parent" durch parent
404// gekennzeichnet wird oder notfalls das Ref-Objekt selber. Zu beachten ist,
405// dass find_base() ggf. rekursiv arbeitet, um sowas wie "riss an kerze in
406// kerzenstaender auf tisch" zu finden. detail ist dann der letzte Teil der
407// tokens (der Anfang!), was keinen Container mehr bezeichnet, das gefundene
408// Objekt ist der letzte Container/Item auf dem Weg zu detail.
409// detail muss als Referenz uebergeben werden.
410// transparent_check ist die Closure, die prueft, ob man in einen
411// Container reinforschen (sehen, lesen, riechen, tasten etc.) kann. Sie
412// bekommt den Container uebergeben.
413private object find_base(object env, string *tokens, string detail,
414 closure transparent_check)
Zesstra55f94fe2018-11-14 23:16:01 +0100415{
Zesstraa19328a2018-11-16 01:28:20 +0100416 // zuerst pruefen, ob man in env reinsehen/reinforschen kann (in
417 // this_object() und environment() kann man immer reinsehen).
418 if (env && env != this_object() && env != environment())
MG Mud User88f12472016-06-24 23:31:02 +0200419 {
Zesstraa19328a2018-11-16 01:28:20 +0100420 // Nicht in Lebewesen reingucken und nicht in intransparente/geschlossene
421 // Container
422 if (living(env) || !funcall(transparent_check, env))
MG Mud User88f12472016-06-24 23:31:02 +0200423 {
Zesstraa19328a2018-11-16 01:28:20 +0100424 // suche nach Objekten in env ist jetzt zuende, weil nicht in env
425 // reinsehbar.
426 // Aber tokens koennte noch ein detail beschreiben, was der Aufrufer an
427 // env findet. Also tokens nach detail und env zurueckgeben.
428 detail = implode(tokens, " ");
Zesstrabd66df72019-04-04 20:53:43 +0200429 DEBUG(PL, sprintf("Env: %O, Detail: %s\n",env, detail),
430 "find_base 1");
Zesstraa19328a2018-11-16 01:28:20 +0100431 return env;
MG Mud User88f12472016-06-24 23:31:02 +0200432 }
MG Mud User88f12472016-06-24 23:31:02 +0200433 }
Zesstraa19328a2018-11-16 01:28:20 +0100434 // ansonsten schauen, ob in tokens noch ein (weiterer) Container beschrieben
435 // wird.
436 for (int i = sizeof(tokens)-1; i > 1; i--)
Zesstra55f94fe2018-11-14 23:16:01 +0100437 {
Zesstradc0ef892018-11-18 23:36:03 +0100438 object ob, *objs;
Zesstraa19328a2018-11-16 01:28:20 +0100439 // In env oder mir oder umgebung gucken, ob ein passendes Objekt da ist.
440 // Hierfuer werden mit jedem Schleifendurchlauf von hinten mehr worte aus
441 // tokens benutzt. (Beispiel "muenze in boerse 2")
Zesstradc0ef892018-11-18 23:36:03 +0100442 // present waere schneller und die Komplexitaet von present_objects waere
443 // hier nicht noetig. Allerdings beruecksichtigt es die Sichtbarkeit und
444 // ggf. virtuell anwesende Objekte, was present nicht tut.
445 string suchid=implode(tokens[i..], " ");
Zesstraa19328a2018-11-16 01:28:20 +0100446 if (env)
Zesstradc0ef892018-11-18 23:36:03 +0100447 objs = env->present_objects(suchid) || ({});
Zesstraa19328a2018-11-16 01:28:20 +0100448 else
Zesstradc0ef892018-11-18 23:36:03 +0100449 objs = environment()->present_objects(suchid) ||
450 this_object()->present_objects(suchid);
451 // wenn nicht genau ein Objekt gefunden wird, wird weitergesucht, im
452 // naechsten Durchlauf mit einem Wort mehr aus tokens
Zesstraa81a17b2018-12-03 21:25:21 +0100453 if (!sizeof(objs))
Zesstradc0ef892018-11-18 23:36:03 +0100454 {
Zesstraa81a17b2018-12-03 21:25:21 +0100455 continue;
456 }
Zesstra9298c2d2018-12-03 21:32:59 +0100457 // falls mehr als ein Objekt gefunden wurden: das erste nehmen, wie
458 // ueberall sonst auch.
Zesstraa81a17b2018-12-03 21:25:21 +0100459 ob = objs[0];
Zesstraa19328a2018-11-16 01:28:20 +0100460
461 // an dieser Stelle wird (noch) nicht geprueft, ob man in das gefundene
462 // Objekt hineinforschen kann.
463
464 // Wenn passende Praeposition davor (z.B. "in" (beutel), "auf" (tisch)),
465 // rekursiv weitersuchen, ob der restliche Teil von tokens evtl. nochmal
466 // nen container in dem gefundenen Objekt bezeichnet.
467 if (tokens[i-1] == ob->QueryProp(P_PREPOSITION))
468 return find_base(ob, tokens[..i-2], &detail, transparent_check);
469
470 // Wenn tokens[i-1] "an" oder "am" ist, wird zwar nicht weiter verzweigt,
471 // aber vermutlich wird ein Detail an diesem Objekt gesucht, d.h. das
472 // Objekt wird env, tokens entsprechend gekuerzt und die Schleife beendet.
473 // Bemerkung: haette ob tatsaechlich eine Praeposition an|am gehabt,
474 // waeren wir vorher in find_base() verzweigt zum Suchen in dem Container.
475 if (tokens[i-1] == "an" || tokens[i-1] == "am") {
476 env = ob;
477 tokens=tokens[..i-2];
478 break;
479 }
Zesstra55f94fe2018-11-14 23:16:01 +0100480 }
Zesstraa19328a2018-11-16 01:28:20 +0100481 // an diesem Punkt sind in tokens nur noch worte drin, die keinen Container
482 // mehr beschreiben. Die Resttokens kommen nach <detail>, sie beschreiben
483 // dann entweder ein Objekt oder ein Detail.
484 detail = implode(tokens, " ");
485
486 // Wenn es ein env gibt, beschreibt detail nun ein Objekt in env oder ein
487 // Detail an env, was der Aufrufer prueft.
488 // Wenn es aber kein env gibt (das passiert, wenn der Aufrufer dieser
489 // Funktion schon kein env (->Referenzobjekt) hereingegeben hat, wurde auch
490 // kein Container gefunden, der durch die tokens beschrieben wurde. Dann
491 // kann der Aufrufer noch an/in this_object() oder environment() gucken, ob
492 // was zu finden ist.
Zesstrabd66df72019-04-04 20:53:43 +0200493 DEBUG(PL, sprintf("Env: %O, Detail: %s\n",env, detail),
494 "find_base 2:");
Zesstraa19328a2018-11-16 01:28:20 +0100495 return env;
Zesstra55f94fe2018-11-14 23:16:01 +0100496}
MG Mud User88f12472016-06-24 23:31:02 +0200497
Zesstra55f94fe2018-11-14 23:16:01 +0100498private nosave int exa_cnt;
499private nosave int exa_time;
500private nosave string *exa;
501private void unt_script_dings(string str)
502{
503 // unt-script-sucher
504 if (!sizeof(str)) return;
505 if (member(({"boden","decke","wand","waende"}),old_explode(str," ")[0]) == -1) {
506 exa_cnt -= (time() - exa_time)/2;
507 exa_time = time();
508 exa_cnt++;
509 if (!exa)
510 exa = ({ str });
511 else
512 exa += ({ str });
513 if (exa_cnt > 10) {
514 log_file("ARCH/LOOK",
515 sprintf("%s: %s in %s\n%@O\n",dtime(time()),getuid(this_object()),
516 environment() ? object_name(environment()) : "???",exa), 150000);
517 exa_cnt = 0;
518 exa = ({});
519 }
520 else if (exa_cnt < 0) {
521 exa_cnt = 0;
522 exa = ({});
523 }
524 }
525}
526
Zesstraf5621a22018-12-06 21:27:21 +0100527private string nf_meldung(string detail, int sense)
Zesstra481215f2018-12-05 22:15:26 +0100528{
529 switch(sense)
530 {
531 case SENSE_SMELL:
Zesstraf5621a22018-12-06 21:27:21 +0100532 return "Du riechst nichts Besonderes.\n";
Zesstra481215f2018-12-05 22:15:26 +0100533 case SENSE_SOUND:
Zesstraf5621a22018-12-06 21:27:21 +0100534 return "Du hoerst nichts Besonderes.\n";
Zesstra481215f2018-12-05 22:15:26 +0100535 case SENSE_TOUCH:
Zesstraf5621a22018-12-06 21:27:21 +0100536 return "Du fuehlst nichts Besonderes.\n";
Zesstra481215f2018-12-05 22:15:26 +0100537// case SENSE_READ:
538// default:
539// return "siehst";
540 }
Zesstraf5621a22018-12-06 21:27:21 +0100541 return sprintf("\'%s\' siehst Du da nicht!\n",capitalize(detail));
Zesstra481215f2018-12-05 22:15:26 +0100542}
543
Zesstrafc501e12018-12-02 23:37:33 +0100544private int _examine_rec(object *ref_objects, string *tokens, closure
Zesstraa19328a2018-11-16 01:28:20 +0100545 transparent_check, int sense, int mode,
546 int ref_given)
547{
Zesstrafc501e12018-12-02 23:37:33 +0100548 // ref_objects sind alle in Frage kommenden Kandidaten, an denen wir suchen.
Zesstrabd66df72019-04-04 20:53:43 +0200549 DEBUG(PL, sprintf("Refobjekt-Kandidaten: %O\n",ref_objects),
550 "_examine_rec 1:");
Zesstrafc501e12018-12-02 23:37:33 +0100551
Zesstraa19328a2018-11-16 01:28:20 +0100552 // Sodann ein Basisobjekt finden. Das Basisobjekt ist das, was bei einem
553 // "detail an parent" durch parent gekennzeichnet wird oder notfalls das
554 // Ref-Objekt von oben. Zu beachten ist, dass find_base() ggf. rekursiv
555 // arbeitet, um sowas wie "riss an kerze in kerzenstaender auf tisch" zu
556 // finden. detail ist dann das letzte Teil der tokens, der keinen Container
557 // mehr bezeichnet, das gefundene Objekt ist der letzte Container/Item auf
558 // dem Weg zu detail. (detail==riss, base==kerze)
559 string detail; // detail an parent
560 // das fuer parent gefundene objekt
Zesstrafc501e12018-12-02 23:37:33 +0100561 object base = find_base(ref_objects[0], tokens, &detail, transparent_check);
562 // BTW: find_base liefert immer was zurueck - wenn nix gefunden wird, das
563 // ref_object, was als Start der Suche uebergeben wurde.
Zesstraa19328a2018-11-16 01:28:20 +0100564 // Jetzt wird versucht, ein Objekt mit der ID "detail" in base oder
Zesstrafc501e12018-12-02 23:37:33 +0100565 // ein Detail an base zu finden.
566
Zesstraa19328a2018-11-16 01:28:20 +0100567 object *objs;
Zesstrafc501e12018-12-02 23:37:33 +0100568 // Wenn wir in base reingucken koennen, ermitteln wir alle zu
Zesstraa19328a2018-11-16 01:28:20 +0100569 // detail in Frage kommenanden Objekte in seinem Inventar
Zesstrafc501e12018-12-02 23:37:33 +0100570 if (base == this_object() || base == environment() ||
571 (funcall(transparent_check, base) && !living(base)))
Zesstraa19328a2018-11-16 01:28:20 +0100572 {
Zesstrafc501e12018-12-02 23:37:33 +0100573 objs = base->locate_objects(detail, 1) || ({});
Zesstraa19328a2018-11-16 01:28:20 +0100574 }
Zesstraa19328a2018-11-16 01:28:20 +0100575 else
576 {
Zesstrafc501e12018-12-02 23:37:33 +0100577 // Basisobjekt da, aber nicht reinguckbar, also keine Objekte
578 // gefunden. base aber nicht nullen, denn es ist ja noch gueltig fuer
579 // Detailsuchen an base...
580 objs = ({});
Zesstraa19328a2018-11-16 01:28:20 +0100581 }
582 // Und in jedem Fall werden alle Objekt raussortiert, die unsichtbar sind.
583 // ;-)
584 objs = filter_objects(objs, "short"); // nur sichtbare...
585
586 // Wenn es sichtbare gibt, werden die ggf. angeguckt.
587 if (sizeof(objs))
588 {
589 // Objekte gefunden, mal schauen, ob die taugen (d.h. fuer den jew. Sinn
590 // Infos haben. Wenn nicht, muessen wir weitersuchen.
591 // Aber erstmal die Objekte durchlaufen.
592 foreach(object ob: objs)
593 {
594 string out;
595 if (sense == SENSE_VIEW)
596 {
597 out = ob->long(mode);
598 }
599 else if (sense == SENSE_READ)
600 {
601 // Extrawurst: P_READ_MSG auch noch abfragen.
602 out = ob->QueryProp(P_READ_MSG);
603 if (!stringp(out))
604 out = ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),SENSE_READ);
605 }
606 else
607 out=ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),sense);
608 // Wenn was gefunden wurde, sind wir fertig.
609 if (stringp(out))
610 {
611 SetProp(P_REFERENCE_OBJECT, ob);
612 tell_object(ME, out);
613 return 1;
614 }
615 }
616 }
617 // keine Objekte gefunden oder die hatten nix fuer unseren Sinn
618 // dabei. Also nach ordinaeren Details an base/parent suchen.
619 string out = base->GetDetail(detail, QueryProp(P_REAL_RACE), sense);
620 // Achja, Tueren gibt es ja auch noch. :-o (TODO: die solten vItems werden)
621 if (!out && sense==SENSE_VIEW)
622 out = base->GetDoorDesc(detail);
623 if (out)
624 {
625 // Detail gefunden, base darf neues Referenzobjekt werden und nach
626 // Ausgabe sind wir fertig.
627 SetProp(P_REFERENCE_OBJECT, base);
628 write(out);
629 return 1;
630 }
631 else
632 {
633 // wenn auch keine Details gefunden, dann schauen, unser Env evtl.
634 // noch nen passendes Detail oder Objekt hat. Das ist aber natuerlich
635 // nur der Fall, wenn ref_object nicht eh schon das Environment war.
636 // Ausserdem nicht machen, wenn der Nutzer ein spezifisches Referenzobjekt
637 // angegeben hat (z.B. "in raum").
Zesstrafc501e12018-12-02 23:37:33 +0100638 if (sizeof(ref_objects) > 1)
Zesstraa19328a2018-11-16 01:28:20 +0100639 {
Zesstrafc501e12018-12-02 23:37:33 +0100640 // Wir schauen uns ggf. weitere Kandidaten an. Bei den anderen
Zesstraa19328a2018-11-16 01:28:20 +0100641 // Sinnen kein neues notify_fail, weil sonst staendig Meldungen kommen,
642 // dass x nicht da ist, nur weil es keine Beschreibung fuer den Sinn hat.
Zesstraefc5b302018-12-14 02:00:58 +0100643 if (sense==SENSE_VIEW || sense==SENSE_READ)
Zesstrafc501e12018-12-02 23:37:33 +0100644 {
Zesstra9a083752018-12-05 20:18:23 +0100645 // Ausgabe, was gerade angeguckt hat.
646 if (ref_objects[0] == environment())
647 out="Sowas siehst Du hier nicht. ";
648 else if (ref_objects[0] == this_object())
649 out="Sowas siehst Du an Dir nicht. ";
650 else
651 out="Sowas siehst Du an/in "+ref_objects[0]->name(WEM) +" nicht. ";
652 // Ausgabe, was man als naechstes anguckt.
653 if (ref_objects[1] == environment())
654 out+="Du schaust Dich um.";
655 else
656 out+="Du schaust Dich an.";
657
658 write(break_string(out,78));
Zesstrafc501e12018-12-02 23:37:33 +0100659 }
660 return _examine_rec(ref_objects[1..], tokens, transparent_check,
661 sense, mode, 0);
Zesstraa19328a2018-11-16 01:28:20 +0100662 }
663 // Leider ist nix mehr uebrig zum angucken und in jedem Fall Ende.
664 else
665 {
666 // Wenn spezifisches ref_object vom Nutzer angegeben (ref_given!=0),
667 // gibt es eine leicht andere Meldung und das Bezugsobjekt muss weg.
Zesstraa19328a2018-11-16 01:28:20 +0100668 if (ref_given) {
669 SetProp(P_REFERENCE_OBJECT, 0);
Zesstraf5621a22018-12-06 21:27:21 +0100670 notify_fail(break_string(nf_meldung(detail, sense),78));
Zesstraa19328a2018-11-16 01:28:20 +0100671 }
Zesstra9ed8c9b2018-12-03 21:23:56 +0100672 // Ansonsten gibt es nur nen passendes notify_fail und Ende.
Zesstraa19328a2018-11-16 01:28:20 +0100673 else if (sense==SENSE_VIEW) {
Zesstraf5621a22018-12-06 21:27:21 +0100674 notify_fail(break_string("\'" + capitalize(detail) + "\'"
675 + " siehst Du auch da nicht!\n"),78);
Zesstra9ed8c9b2018-12-03 21:23:56 +0100676 }
677 else {
Zesstraf5621a22018-12-06 21:27:21 +0100678 notify_fail(break_string(nf_meldung(detail, sense),78));
Zesstraa19328a2018-11-16 01:28:20 +0100679 }
680 return 0;
681 }
682 }
683 // Nie erreicht.
684 return 0;
685}
686
Zesstra55f94fe2018-11-14 23:16:01 +0100687varargs int _examine(string str, int mode)
688{
689 // Sinn auswaehlen, der benutzt wird. Wenn -1, abbrechen (kein Sinn bzw.
690 // Sinn erlaubt ggf. kein leeren <str> fuer Defaulmeldung). Je nach Sinn
Zesstraa19328a2018-11-16 01:28:20 +0100691 // wird auch <str> modifiziert.
692 // transparent_check enthaelt spaeter die Closure, die entscheidet,
693 // ob man mit dem Sinn in einen Container reinforschen kann.
694 closure transparent_check;
695 int sense=select_sense(&str, &transparent_check);
Zesstra55f94fe2018-11-14 23:16:01 +0100696 if (sense<0) return 0;
697
698 unt_script_dings(str);
699
700 // Wenn kein str, dann SENSE_DEFAULT vom Environment ausgeben. Bei
Zesstraa19328a2018-11-16 01:28:20 +0100701 // SENSE_VIEW wird das bereits bei den Aufrufern dieser Funktion gemacht und
702 // die Beschreibung der Umgebung ausgegeben.
Zesstra55f94fe2018-11-14 23:16:01 +0100703 if (sense!=SENSE_VIEW)
704 {
705 if (!str)
706 {
707 string detail = environment()->GetDetail(SENSE_DEFAULT,
708 QueryProp(P_REAL_RACE),sense);
709 if(!detail)
710 return 0;
711 write(detail);
712 return 1;
713 }
714 }
715
Zesstraa19328a2018-11-16 01:28:20 +0100716 string *tokens = explode(str, " ");
717 int ref_given;
718 // Das Ref-Objekt finden, das kann dann auch in tokens angegeben sein. In dem
719 // Fall wird die Liste an tokens um die Angabe des Ref-Objekts gekuerzt und
720 // ref_given gesetzt.
Zesstrafc501e12018-12-02 23:37:33 +0100721 object ref_ob = get_ref_object(&tokens, transparent_check, &ref_given);
Zesstraa19328a2018-11-16 01:28:20 +0100722 // Bemerkung: Das Referenzobjekt ist garantiert sichtbar, aber nicht
723 // unbedingt einsehbar. Wird weiter unten geprueft.
Zesstra55f94fe2018-11-14 23:16:01 +0100724
Zesstrafc501e12018-12-02 23:37:33 +0100725 // Falls nicht explizit ein Objekt als Ref-Objekt angegeben wurde, werden
726 // max. 3 Objekte angeguckt: das eigentliche Referenzobjekt, das
727 // Environment und das eigene Objekt. Da ref_object auch eines der letzten
728 // beiden sein kann, muss die Dublette ggf. raus.
729 object *ref_objects;
730 if (ref_ob)
731 {
732 ref_objects=({ref_ob});
733 if (!ref_given)
734 ref_objects += ({environment(), this_object()}) - ref_objects;
735
736 }
737 else
738 ref_objects = ({environment(), this_object()});
739
740 return _examine_rec(ref_objects, tokens, transparent_check, sense, mode,
741 ref_given);
MG Mud User88f12472016-06-24 23:31:02 +0200742}
743
Zesstra55f94fe2018-11-14 23:16:01 +0100744// Funktion fuer "schau in ..."
MG Mud User88f12472016-06-24 23:31:02 +0200745varargs int look_into(string str,int mode)
746{
747 object *found_obs;
748
749 if( CannotSee() ) return 1;
Zesstra55f94fe2018-11-14 23:16:01 +0100750 _notify_fail("Wo willst Du denn reinschauen?\n");
751 found_obs=find_objects(str, 0, 0);
752 if (!sizeof(found_obs))
MG Mud User88f12472016-06-24 23:31:02 +0200753 {
754 if (environment() &&
755 (environment()->GetDetail(str,QueryProp(P_REAL_RACE))||
756 environment()->GetDoorDesc(str)))
757 _notify_fail("Da kannst Du so nicht reinsehen.\n");
758 return 0;
759 }
Zesstra55f94fe2018-11-14 23:16:01 +0100760
MG Mud User88f12472016-06-24 23:31:02 +0200761 return _examine(str, mode);
762}
763
764/* Gebe die Umgebung des aktiven Spielers zurueck, lasse dabei */
765/* rekursiv geschachtelte Raeume zu. */
766/* Wenn allow_short 0 ist, so wird immer die long-descr benutzt */
767varargs string env_descr(int allow_short,int flags, int force_short )
768{
769 object env;
770 int brief;
771
772 env = environment(ME);
773
774 if(!env)
775 return "Du schwebst im Nichts ... Du siehst nichts, rein gar nichts ...\n";
776
777 if (!force_short && (!allow_short || !QueryProp(P_BRIEF)))
778 return env->int_long(ME,ME,flags);
779
780 if (!flags && ((brief=QueryProp(P_BRIEF))>=2))
781 return "";
782
783 return env->int_short(ME,ME);
784}
785
Zesstra55f94fe2018-11-14 23:16:01 +0100786// Kommandofunktion fuer schau.
787// Verzweigt ggf. in _examine() fuers normale Untersuchen.
MG Mud User88f12472016-06-24 23:31:02 +0200788int _look(string str)
789{
790 string s;
791 int flag;
792
793 if(CannotSee()) return 1;
794
Zesstra55f94fe2018-11-14 23:16:01 +0100795 // nur schau mit ggf. Flags entsorgt das Ref-Objekt und schaut die
796 // Raumbeschreibung an.
MG Mud User88f12472016-06-24 23:31:02 +0200797 if(!str)
798 {
799 SetProp(P_REFERENCE_OBJECT, 0);
800 write( env_descr() );
801 return 1;
802 }
803 if(str=="-f" || str=="genau")
804 {
805 SetProp(P_REFERENCE_OBJECT, 0);
806 write( env_descr(0,2) );
807 return 1;
808 }
809 if(str=="-k" || str=="kurz")
810 {
811 SetProp(P_REFERENCE_OBJECT, 0);
812 write( env_descr(1,2,1) );
813 return 1;
814 }
Zesstra55f94fe2018-11-14 23:16:01 +0100815 // Ansonsten wird in _examine() weitergemacht, wobei Flags vorher
816 // rausgeschnitten werden. _examine() benutzt die Flags, falls es eine
817 // Langbeschreibung eines Objektes ausgeben wird.
MG Mud User88f12472016-06-24 23:31:02 +0200818 if(str[0..2]=="-f "){
819 flag=2;
820 str=str[3..];
821 }
822 else if(str[0..5]=="genau "){
823 flag=2;
824 str=str[6..];
825 }
826 else flag = 0;
Zesstra55f94fe2018-11-14 23:16:01 +0100827
MG Mud User88f12472016-06-24 23:31:02 +0200828 if (sscanf(str,"%s an",s)) str=s;
Zesstra55f94fe2018-11-14 23:16:01 +0100829 // "in mir", "in dir" soll in _examine rein, aber "in ..." in look_into().
MG Mud User88f12472016-06-24 23:31:02 +0200830 if (sscanf(str,"%s in mir",s)||sscanf(str,"%s in dir",s)) return _examine(str,flag);
831 if (sscanf(str,"in %s",s)) return look_into(s,flag);
Zesstra55f94fe2018-11-14 23:16:01 +0100832 // Alles andere weiter an _examine().
MG Mud User88f12472016-06-24 23:31:02 +0200833 return _examine(str,flag);
834}
835
836int _equipment(string arg)
837{
838 if (CannotSee()) return 1;
839 call_other("/std/player/invmaster/invmaster", "ShowInv", ME, arg);
840 return 1;
841}
842
843static mixed _query_localcmds()
844{
845 return
846 ({({"ausruestung", "_equipment",0,0}),
847 ({"i","_inventory",0,0}),
848 ({"inv","_inventory",0,0}),
849 ({"inventur","_inventory",0,0}),
850 ({"schau","_look",0,0}),
851 ({"schaue","_look",0,0}),
852 ({"unt","_examine",0,0}),
853 ({"untersuch","_examine",0,0}),
854 ({"betracht","_examine",0,0}),
855 ({"untersuche","_examine",0,0}),
856 ({"betrachte","_examine",0,0}),
857 ({"betr","_examine",0,0}),
Zesstra55f94fe2018-11-14 23:16:01 +0100858 ({"lausche","_examine",0,0}),
859 ({"lausch","_examine",0,0}),
860 ({"hoer","_examine",0,0}),
861 ({"hoere","_examine",0,0}),
862 ({"lies","_examine",0,0}),
863 ({"lese","_examine",0,0}),
864 ({"les","_examine",0,0}),
865 ({"schnupper","_examine",0,0}),
866 ({"schnuppere","_examine",0,0}),
867 ({"riech","_examine",0,0}),
868 ({"rieche","_examine",0,0}),
869 ({"taste","_examine",0,0}),
870 ({"beruehre","_examine",0,0}),
871 ({"beruehr","_examine",0,0}),
MG Mud User88f12472016-06-24 23:31:02 +0200872 ({"kurz","_toggle_brief",0,0}),
873 ({"lang","_toggle_brief",0,0}),
874 ({"ultrakurz","_toggle_brief",0,0})
875 });
876}