blob: e22ce1795db3f0b0481f879ab18a549cef117570 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001//--------------------------------------------------------------------------------
2// Name des Objects: Aufbewahrungtruhe fuer Autoload-Objekte
3//
4// Magier: Zesstra
5//--------------------------------------------------------------------------------
6#pragma strong_types,rtt_checks
7
8#include "schrankladen.h"
9inherit LADEN("swift_std_container");
10
11//#include <ansi.h>
12#include <class.h>
13#include <wizlevels.h>
14#include "/d/seher/haeuser/haus.h"
15
16#define IDS 0
17#define NAME 1
18#define ALDATA 2
19
20#define LOG(x,y) log_file(x,sprintf("%s [%s, %O, %O]: %s\n",dtime(time()),\
21 (uuid?uuid:" "),PL,object_name(ME),y))
22#define STORELOG(x) LOG("zesstra/ALTRUHE_STORE.log",x)
23#define PICKLOG(x) LOG("zesstra/ALTRUHE_PICK.log",x)
24
25#define ITEMLOG(x) log_file("zesstra/ALTRUHE_ITEMS.log",\
26 sprintf("%s [%O]: %s\n",dtime(time()),\
27 this_interactive()||PL,x))
28
29#define ERRLOG(x) LOG("zesstra/ALTRUHE.ERR",x)
30
31#undef BS
32#define BS(x) break_string(x,78)
33
34#define VERSION_OBJ "5"
35
36#ifdef MAINTAINER
37#undef MAINTAINER
38#endif
39#define MAINTAINER ({"zesstra"})
40
41// Savefile der Blueprint
42#ifdef SAVEFILE
43#undef SAVEFILE
44#endif
45#define SAVEFILE __FILE__[..<3]
46
47#define DEBUG(x) if (funcall(symbol_function('find_player),MAINTAINER[0]))\
48 tell_object(funcall(symbol_function('find_player),MAINTAINER[0]),\
49 "ALTruhe: "+x+"\n")
50
51#define ACCESS (my_secure_level() >= ARCH_LVL)
52
53// Diese 4 sind fuer die Blueprint (Master aller Truhen)
54mapping whitelist=([]); // erlaubte Autoloader, alle anderen nicht erlaubt.
55mapping blacklist=([]); // bereits explizit durch EM+ abgelehnte Autoloader
56mapping vorschlaege=m_allocate(1,2); // vorschlaege der Spieler
57mapping data=([]); // hier speichert die BP alle Daten der Truhen
58 // WICHTIG: dieses Mapping wird in /secure/memory
59 // abgelegt und muss auch beim Neuladen ggf. von dort
60 // wieder abgeholt werden. Ausserdem teilen sich alle
61 // Truhen eines Spielers und diese Blueprint die Mappings
62 // darin, damit Aenderungen sofort in allen Truhen und dem
63 // Master bekannt sind.
64
65/* die einzelnen Truhen speichern in autoloader. Format:
66 3 Werte pro Key, Keys sind die Namen der Blueprints der Objekte:
67 ([<blueprint>: <P_IDS>; <ob->name()>; <P_AUTOLOADOBJ> ])
68 */
69nosave mapping autoloader=m_allocate(1,3);
70nosave string uuid; // UUID des Eigentuemers
71nosave object ob_in_bewegung; // uebler Hack eigentlich. :-(
72
73void NotifyInsert(object ob, object oldenv);
74static mapping QueryData();
75static mapping SetData(mixed data);
76protected void save_me();
77protected void check_content();
78
79nomask private int my_secure_level(); //Args.
80
81protected void create() {
82
83 // Ja, es is Absicht, dass das create der BP erst spaeter abgebrochen wird!
84 swift_std_container::create();
85
86 seteuid(getuid(ME));
87
88 // falls dies die BP ist:
89 // 1. das Savefile zu laden.
90 // 2. versuchen, die truhendaten von /secure/memory zu holen, damit nach dem
91 // Neuladen der Master und die Client immer nach _dieselben_ Mappings
92 // haben.
93 if (!clonep(ME))
94 {
95 // Savefile restaurieren (auch wenn data im Memory ist, muss das sein,
96 // damit die anderen Variablen wieder eingelesen sind).
97 restore_object(SAVEFILE);
98 // jetzt Daten aus Memory holen, sofern verfuegbar
99 mapping tmp = "/secure/memory"->Load("truhendaten");
100 if (mappingp(tmp))
101 {
102 // Daten aus Savefile durch die vom Memory ersetzen
103 data = tmp;
104 }
105 else
106 {
107 // Keine Daten in Memory. Jetzt in jedem Fall den Pointer auf das Mapping data in
108 // /secure/memory ablegen
109 if ("/secure/memory"->Save("truhendaten",data) != 1)
110 {
111 raise_error("Could not save memory to /secure/memory.");
112 }
113 }
114 }
115 else
116 {
117 // brauchen Clones nicht.
118 set_next_reset(-1);
119 data=0; // brauchen Clones nicht.
120 }
121
122 SetProp(P_SHORT, "Eine kleine, magische Holztruhe");
123 SetProp("cnt_version_obj", VERSION_OBJ);
124 SetProp(P_NAME, "Holztruhe");
125 SetProp(P_GENDER, FEMALE);
126 SetProp(P_NAME_ADJ,({"klein", "magisch"}));
127 SetProp(P_LONG, BS(
128 "Die kleine Holztruhe ist aus stabilem Eichenholz gefertigt. Eigentlich "
129 "saehe sie recht unscheinbar aus, waeren da nicht die vielen kunstvollen "
130 "Runen an den Seiten und auf dem Deckel.")
131 +"@@cnt_status@@");
132
133 AddId(({"autoloadertruhe", "holztruhe", "truhe"}));
134
135 // den Rest vom Create braucht die BP nicht.
136 if (!clonep(ME)) return;
137
138 SetProp(P_LOG_FILE,"zesstra/ALTRUHE.rep");
139
140 SetProp(P_WEIGHT, 3000); // Gewicht 5 Kg
141 // die drei hier sind in diesme Fall eigentlich voellig ohne Bedeutung
142 SetProp(P_MAX_WEIGHT, 1000000); // Es passen fuer 1000 kg Sachen rein.
143 SetProp(P_WEIGHT_PERCENT, 100);
144 SetProp(P_MAX_OBJECTS, 100); // sind eh immer 0 echte Objekte drin.
145
146 SetProp(P_VALUE, 0); // Kein materieller Wert. Ist eh nicht verkaufbar.
147 SetProp(P_NOBUY, 1); // Wird im Laden zerstoert, falls er verkauft wird.
148 SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
149 SetProp(P_MATERIAL, ([MAT_OAK:49, MAT_MISC_MAGIC:50, MAT_JOFIUM: 1]) );
150 SetProp(P_INFO, BS("In diese stabile Truhe kannst Du bestimmte "
151 "Autoload-Objekte hineinlegen und sie lagern, wenn Du sie gerade "
152 "nicht brauchst. (Mit 'deponiere <was>' kannst Du etwas in der "
153 "Truhe zur Aufbewahrung deponieren, mit 'entnehme <was> oder "
154 "'nimm <was> aus truhe' kannst Du einen Gegenstand wieder "
155 "herausnehmen.)"));
156
157 // Prop fuer rebootfeste Moebel
158 // Der endgueltige Wert (UUID des Spielers) wird per SetBesitzer() gesetzt,
159 // sobald die Truhe das erste Mal in ein Seherhaus bewegt wurde.
160 SetProp(H_FURNITURE, 1);
161
162 AD(({"platz","groesse"}),
163 BS("Die Truhe ist recht klein, aber merkwuerdigerweise "
164 "scheint sie viel mehr an Gegenstaenden aufnehmen zu koennen, als "
165 "es von ihrer Groesse her scheint."));
166 AD(({"gegenstand","gegenstaende"}),
167 BS("Die Truhe scheint zwar vielen Gegenstaenden Platz zu bieten, aber "
168 "dafuer nimmt sie nicht jeden Gegenstand auf."));
169 AD(({"holz","eichenholz"}),
170 BS("Das Eichenholz ist sehr stabil. Es ist ganz glatt geschliffen und "
171 "traegt viele kunstvolle Runen in sich."));
172 AD(({"seiten","seite"}),
173 BS("Die Truhe hat 4 Seiten, die allesamt mit Runen verziert sind."));
174 AD(({"boden"}),
175 BS("Der Boden der Truhe traegt keinerlei Runen."));
176 AD(({"deckel"}),
177 BS("Auch der Deckel der Truhe ist mit Runen verziert."));
178 AD(({"runen","rune"}),
179 BS("Die Runen bedecken alle 4 Seiten und den Deckel der Truhe. Man "
180 "hat sie zweifellos in muehsamer Arbeit aus dem harten Holz "
181 "geschnitzt. Anschliessend wurden sie offenbar mit einem "
182 "Metall gefuellt. Du verstehst zwar ueberhaupt nicht, was "
183 "die Runen bedeuten, aber sie sind bestimmt magisch, denn wann immer "
184 "Du den Deckel oeffnest oder schliesst oder Gegenstaende hineinlegst "
185 "oder herausnimmst, leuchten die Runen hell auf."));
186 AD(({"metall"}),
187 BS("Was das wohl fuer ein Metall sein mag? Zweifellos hat es auch was "
188 "mit Magie zu tun."));
189 AD(({"magie"}),
190 BS("In dieser Truhe scheint viel Magie zu stecken, wenn selbst ein "
191 "Weltuntergang ihr nichts anhaben kann."));
192 AD(({"arbeit","fertigung"}),
193 BS("Die Fertigung dieser Truhe muss sehr aufwendig gewesen sein. "
194 "Kein Wunder, dass die Truhe so teuer ist."));
195 AD(({"wunder"}),
196 BS("Ein Wunder scheint es zu sein."));
197 AD(({"weltuntergang","armageddon"}),
198 BS("Dir schaudert beim Gedanken an den Weltuntergang."));
199 AD(({"gedanken"}),
200 BS("Denk doch lieber an was anderes..."));
201
202 AddCmd("deponier|deponiere&@PRESENT","cmd_deponiere",
203 "Was moechtest Du in der Eichenholztruhe deponieren?");
204 AddCmd("entnimm|entnehme|entnehm","cmd_entnehmen");
205
206 // bei dieser Truhe waere das Erlauben voellig sinnlos. ;-)
207 RemoveCmd(({"serlaube"}));
208
209}
210
211// keine Truhen zerstoeren, die irgendeinen INhalt haben.
212int zertruemmern(string str) {
213 // aus swift_std_container
214 string nf_str;
215 nf_str="Syntax: zertruemmer [Objekt-Id]\n"
216 +"Bsp.: zertruemmer "+QueryProp(P_IDS)[1]+"\n";
217 notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str);
218 if(!str) return 0;
219 notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"+nf_str);
220 if(present(str)!=TO) // Ueberpruefe, ob auch dieses Objekt gemeint ist!
221 return 0;
222 if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
223 {
224 write( BS("Nur "+QueryHausbesitzer()+" darf "+name(WEN,1)+" zertruemmern!"));
225 return 1;
226 }
227 // Objekte enthalten? Wenn ja, abbruch.
228 if (sizeof(autoloader)) {
229 tell_object(PL,BS("Du willst gerade zum Schlag ausholen, um "
230 +name(WEN,1)+ " zu zertruemmern, als Dir einfaellt, dass "
231 +QueryPronoun(WER)+ " ja gar nicht leer ist! Nene, wer weiss, ob "
232 "Du das nicht noch brauchen koenntest."));
233 return 1;
234 }
235 // sonst geerbten Kram ausfuehren.
236 return ::zertruemmern(str);
237}
238
239// Zesstra, 1.7.07, fuers Hoerrohr
240string GetOwner() {return "zesstra";}
241
242// Prueft das Objekt auf Eignung fuer die Truhe, speichert seine Daten und
243// zerstoert es anschliessend. NODROP-Objekte koennen nur so in die Truhe
244// gelegt werden, alle anderen koennen auch mit "stecke ... in truhe"
245// deponiert werden, woraufhin ebenfalls PreventInsert() und NotifyInsert()
246// durchlaufen werden.
247protected int cmd_deponiere(string cmd, mixed args) {
248 if (!objectp(PL)) return 0;
249
250 notify_fail(Name(WER,1)+" ist doch geschlossen!\n");
251 if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN) return 0;
252
253 notify_fail("Was moechtest Du in der Eichenholztruhe deponieren?\n");
254 if (!stringp(cmd) || !sizeof(cmd)
255 || !pointerp(args) || !sizeof(args)) return 0;
256 object ob=args[0];
257 if (!objectp(ob)) return 0;
258 // wuerde die Truhe das Objekt ueberhaupt aufnehmen? Fehlerausgabe durch
259 // PrevenInsert()
260 if (PreventInsert(ob)) return 1;
261 // Ausziehen... Schade, dass DoUnwear nix vernuenftiges an Rueckgabewert
262 // hat. :-(
263 if (objectp(ob->QueryProp(P_WORN))) {
264 ob->DoUnwear();
265 if (objectp(ob->QueryProp(P_WORN))) {
266 tell_object(PL, BS("Du musst "+ ob->name(WEN,1)+ "zunaechst einmal "
267 "ausziehen!"));
268 return 1;
269 }
270 }
271 // wegstecken
272 if (objectp(ob->QueryProp(P_WIELDED))) {
273 ob->DoUnwield();
274 if (objectp(ob->QueryProp(P_WIELDED))) {
275 tell_object(PL, BS("Du musst "+ ob->name(WEN,1)+ "zunaechst einmal "
276 "wegstecken!"));
277 return 1;
278 }
279 }
280 // NO_CHECK und Silent-Bewegung, Meldungen an den Spieler selber machen.
281 tell_object(PL, BS("Du steckst "+ ob->name(WEN,1) + " in " + name(WEN,1) +
282 "."));
283
284 tell_room(environment(),BS(PL->Name(WER) + " legt " + ob->name(WEN,0) +
285 " in " + name(WEN,1) + " hinein."),({PL}));
286 ob->move(ME, M_NOCHECK|M_SILENT);
287 return 1;
288}
289
290// alternative zum "nimm bla aus truhe". Spieler wollten was kurzes dafuer
291// haben.
292protected int cmd_entnehmen(string cmd) {
293 int res;
294 mixed noget;
295
296 if (!objectp(PL)) return 0;
297
298 notify_fail(Name(WER,1)+" ist doch geschlossen!\n");
299 if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN) return 0;
300
301 notify_fail(BS("Was moechtest Du aus "+name(WEM,1)+ " entnehmen?\n"));
302 if (!stringp(cmd) || !sizeof(cmd)) return 0;
303
304 object *obs=present_objects(cmd);
305
306 if (!sizeof(obs) || !objectp(obs[0]))
307 return 0;
308
309 // NOGET ist hier bloed. So ist es zwar auch nicht richtig doll... *seufz*
310 // Die hier ist/waere aber nen uebler Hack, erstmal auskommentiert lassen.
311 // also, P_NOGET sichern.
312 /*if (!closurep(noget=ob->Query(P_NOGET,F_QUERY_METHOD))) {
313 noget=ob->Query(P_NOGET,F_VALUE);
314 ob->Set(P_NOGET,0,F_VALUE);
315 }
316 else {
317 ob->Set(P_NOGET,0,F_QUERY_METHOD);
318 }*/
319 // nehmen.
320 res=PL->pick_obj(obs[0]);
321 // P_NOGET zurueckschreiben. (Ja, wenn eine Closure als F_VALUE drinsteht,
322 // landet die jetzt als F_QUERY_METHOD im Objekt.
323 /*if (closurep(noget)) {
324 ob->Set(P_NOGET,noget,F_QUERY_METHOD);
325 }
326 else
327 ob->Set(P_NOGET,noget,F_VALUE);
328*/
329 return(res);
330}
331
332// Hier wird auch das PreventInsert() von der Blueprint gerufen. Das
333// erleichtert es, die Liste an erlaubten Objekten zu aendern, ohne dass man
334// alle Clones ersetzen muss.
335varargs int PreventInsert(object ob) {
336 string oname;
337 // Das Objekt, was die Truhe gerade selber bewegt, wird ignoriert.
338 if (!objectp(ob) || ob_in_bewegung==ob) return 0;
339
340 oname=BLUE_NAME(ob);
341
342 // Pruefung in Clonen:
343 if (clonep(ME))
344 {
345 // nur Eigentuemer
346 if (!stringp(uuid) || !sizeof(uuid)) {
347 if (objectp(PL))
348 tell_object(PL,BS(sprintf("%s gehoert Dir nicht, daher kannst Du "
349 "auch keine Gegenstaende in %s legen.",
350 Name(WER,1),QueryPronoun(WEN))));
351 return 1;
352 }
353 if (!objectp(PL) || getuuid(PL)!=uuid) {
354 if (objectp(PL))
355 tell_object(PL,BS(sprintf("Nur %s darf Gegenstaende in %s "
356 "hineinlegen.",capitalize(explode(uuid,"_")[0]),name(WEN,1))));
357 return 1;
358 }
359 // jedes Objekt nur einmal.
360 if (member(autoloader,oname)) {
361 if (objectp(PL))
362 tell_object(PL,BS(Name(WER,1)+ " kann von einem Gegenstand jeweils "
363 "nur ein Exemplar aufnehmen, es ist aber bereits "
364 +ob->name(WER,0) + " in " + QueryPronoun(WEM) + "."));
365 return 1;
366 }
367 // jetzt Erlaubnisliste der BP fragen.
368 if (objectp(blueprint(ME)))
369 return blueprint(ME)->PreventInsert(ob);
370 else
371 return load_object(load_name(ME))->PreventInsert(ob);
372 }
373 // Ende fuer Pruefung fuer Clone.
374
375 // ab hier jetzt die Pruefung durch die BP.
376 // Keine (freigegebener) Autoloader? Hat in diesem Container nix verloren!
377 if( !ob->QueryProp(P_AUTOLOADOBJ) ) {
378 if (objectp(PL))
379 tell_object(PL,BS("In "+name(WEN,1)
380 +" kannst Du nur Autoload-Objekte hineinlegen. "));
381 return 1;
382 }
383 else if (member(blacklist,oname)) {
384 if (objectp(PL))
385 tell_object(PL,BS("In "+name(WEN,1)
386 +" kannst Du nur dafuer erlaubte Autoload-Objekte hineinlegen. "
387 +ob->Name(WER,1) + " wurde explizit als nicht erwuenscht "
388 "befunden."));
389 return 1;
390 }
391 else if (!member(whitelist,oname)) {
392 if (!member(vorschlaege,oname)) {
393 vorschlaege[oname,0]=ob->name(WER,0) || "";
394 vorschlaege[oname,1]=ob->short() || "";
395 }
396 if (objectp(PL))
397 tell_object(PL,BS("In "+name(WEN,1)
398 +" kannst Du nur dafuer erlaubte Autoload-Objekte hineinlegen. "
399 +ob->Name(WER,1) + " ist momentan nicht auf der Liste. Der "
400 "Gegenstand wurde jetzt als Vorschlag gespeichert. Bitte hab "
401 "etwas Geduld, bis sich jemand den Gegenstand angeschaut hat."));
402 // ggf. reset reaktivieren und Maintainer informieren.
403 if (!query_next_reset())
404 set_next_reset(1);
405 return 1;
406 }
407 // getragenes?
408 if (ob->QueryProp(P_WORN)) {
409 ob->DoUnwear(0);
410 if (ob->QueryProp(P_WORN)) { //ARGL. GRUMMEL.
411 if (objectp(PL))
412 tell_object(PL,BS("Willst Du "+ob->name(WEN,1) + " nicht erstmal "
413 "ausziehen?"));
414 return 1;
415 }
416 }
417 // enthaelt es irgendwelche anderen Objekte?
418 if (sizeof(all_inventory(ob))) {
419 if (objectp(PL))
420 tell_object(PL,BS(ob->Name(WER,1) + " ist nicht leer."));
421 return 1;
422 }
423
424 // andere Einschraenkungen, als hier geprueft werden, gibt es nicht.
425 return 0;
426}
427
428// hier ist das Objekt jetzt in der Truhe, d.h. Daten speichern und Objekt
429// destructen. ;)
430void NotifyInsert(object ob, object oldenv) {
431
432 // Das Objekt, was die Truhe gerade selber bewegt, wird ignoriert.
433 if (!objectp(ob) || ob==ob_in_bewegung)
434 return;
435 STORELOG(sprintf("%s deponiert %s [%O], Daten: %O",getuid(PL),ob->name(WEN,0),
436 ob,ob->QueryProp(P_AUTOLOADOBJ)));
437 // noetig sind die IDs, den Namen und die AUTOLOADOBJ-Daten des Objekts.
438 // ;-)
439 autoloader[BLUE_NAME(ob),ALDATA]=ob->QueryProp(P_AUTOLOADOBJ);
440 autoloader[BLUE_NAME(ob),IDS]=ob->QueryProp(P_IDS);
441 // Objekte, die 0 als short liefern, kriegen eine Standard-Short und werden
442 // sichtbar gemacht, da sie sonst nicht wiederzufinden sind. Objekte, die
443 // unsichtbar sein sollen, duerfen nicht erlaubt werden.
444 // TODO eine ID als Short nehmen?
445 autoloader[BLUE_NAME(ob),NAME]=capitalize((ob->short()||"<Unbekannt>.\n")[..<3]);
446 // nach dem Move die realen Objekte destructen.
447 call_out(#'check_content, 0);
448 save_me();
449}
450
451// Objekt wurde entnommen, also aus der Liste der enthaltenen Autoloader
452// loeschen. Ausserdem Objekt konfigurieren. Dies wird erst hier gemacht und
453// nicht in create_object(), damit es so aehnlich wie moeglich zum clonen der
454// Autoloader beim erstellen des Spielerobjektes wird (dort wird erst bewegt,
455// dann konfiguriert).
456void NotifyLeave(object ob, object dest) {
457 string error, oname;
458
459 if (!objectp(ob))
460 return;
461
462 oname=BLUE_NAME(ob);
463 if (!member(autoloader,oname)) {
464 // Das sollte definitiv nicht passieren.
465 ERRLOG(sprintf("Gegenstand (%O) wurde entnommen, der nicht "
466 "gespeichert war!",ob));
467 return;
468 }
469
470 // wenn kein Fehler: Objekt aus Liste loeschen. Sonst wird das Objekt
471 // zerstoert. Damit bleiben die Autoloader-Daten hier erhalten und der
472 // Spieler hat kein disfunktionales Objekt im Inventar.
473 if (error) {
474 ERRLOG(sprintf("Fehler beim Konfigurieren von %O. Fehlermeldung: %O."
475 "Daten: %O",ob,error,autoloader[oname,ALDATA]));
476 ob->remove(1);
477 if (objectp(ob))
478 destruct(ob);
479 }
480 else {
481 PICKLOG(sprintf("Objekt (%O) wurde entnommen.",ob));
482 m_delete(autoloader,oname);
483 // speichern
484 save_me();
485 }
486}
487
488protected void check_content() {
489 // wenn Objekte noch in der Truhe sind, also nicht erfolgreich in
490 // einen Spieler bewegt wurden, werden zerstoert. Datenverlust gibt es
491 // hierbei nicht, weil die Daten der Autoloader nur durch NotifyLeave()
492 // geloescht werden.
493 foreach(object ob: all_inventory()) {
494 ob->remove(1);
495 if (objectp(ob)) destruct(ob);
496 }
497}
498
499// hier nochmal schauen, ob das Objekt auch in den richtigen Spieler bewegt
500// wird... Falls das move dann hier abgebrochen wird, wird das Objekt im
501// naechsten HB von der Truhe zerstoert. (chk_contents())
502varargs int PreventLeave(object ob, mixed dest) {
503 object ob2;
504
505 //DEBUG(sprintf("PreventLeave(): Ob: %O, Dest: %O (%O)",
506 // ob,dest,(objectp(dest)?"Objekt":"Non-Object")));
507
508 if (!objectp(ob)) return 0;
509 // falls string uebergeben, erstmal zug. Spieler finden.
510 if (stringp(dest) && sizeof(dest))
511 dest=find_player(dest);
512
513 // richtiges INteractive? Dann darf das Objekt raus. Sonst nicht. ;-)
514 if (!objectp(dest) || !interactive(dest) || getuuid(dest)!=uuid)
515 return 1;
516
517 // pruefen, ob der Spieler schon ein Objekt dieser Blueprint dabei hat. Wenn
518 // ja, Abbruch, das koennte sonst zuviele Probleme geben.
519 if (objectp(ob2=present_clone(ob,dest))) {
520 tell_object(dest,BS("Du hast bereits "+ob2->name(WEN,0) +
521 " dabei. Zwei Gegenstaende dieser Art kannst du nicht gleichzeitig "
522 "mit Dir herumtragen."));
523 return 1; //nicht rausnehmen.
524 }
525 return 0;
526}
527
528// Objekte ausgeben, die hier angeblich drin sind. ;-) Gibt aber nur die
529// Autoloader aus, keine echten Objekt, die Container sind. Allerdings sollte
530// sowas eh nicht vorkommen. ;-)
531// flags: 1 - return array, 2 - don't collect equal objects '
532// flags: 4 - don't append infos for wizards
533varargs mixed make_invlist(object viewer, mixed inv, int flags) {
534 int iswiz;
535 mixed objs;
536
537 iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
538 // Mapping benutzen, um multiplen Overhead fuer allokation im foreach() zu
539 // vermeiden.
540 objs=m_allocate(sizeof(autoloader),1);
541 foreach(string oname, string *ids, string sh: autoloader) {
542 if (iswiz && !(flags & 4))
543 objs[oname]=sh + ". ["+oname+"]";
544 else
545 objs[oname]=sh + ".";
546 }
547 if(flags & 1) return(m_values(objs)-({""}));
548 if(!sizeof(autoloader)) return "";
549 return sprintf("%"+(sizeof(objs) > 6 ? "#" : "=")+"-78s",
550 implode(m_values(objs)-({""}), "\n")) + "\n";
551}
552
553// erzeugt das benannte Objekt und liefert es zurueck. Liefert 0 im Fehlerfall.
554// ausserdem bewegt es das Objekt in die Truhe, damit es ein Env hat und die
555// Truhe via NotifyLeave() mitkriegt, dass es tatsaechlich entnommen wurde.
556private object create_object(string oname) {
557 string error;
558 object ob;
559 mixed noget;
560 if (!member(autoloader,oname)) return 0;
561
562 //Blueprint finden (ja, das ist nicht unbedingt noetig, man koennte auch
563 //direkt clonen)
564 if (error=catch(ob=load_object(oname);publish) ||
565 !objectp(ob)) {
566 ERRLOG(sprintf("Konnte %s nicht laden/finden. Fehler: %O",
567 oname,error));
568 return 0;
569 }
570 // clonen
571 if (error=catch(ob=clone_object(oname);publish) ||
572 !objectp(ob)) {
573 ERRLOG(sprintf("Konnte %s nicht clonen. Fehler: %O",
574 oname,error));
575 return 0;
576 }
577 // konfigurieren
578 error=catch(ob->SetProp(P_AUTOLOADOBJ,autoloader[oname,ALDATA]);publish);
579
580 //Objekt bewegen, dabei Objekt in glob. Var. merken, damit PreventInsert()
581 //und NotifyInsert() es ignorieren. *seufz*
582 ob_in_bewegung=ob;
583 ob->move(ME,M_NOCHECK);
584 ob_in_bewegung=0;
585
586 // jetzt noch nen Callout starten, damit das Objekt zerstoert wird, wenn es
587 // nicht wirklich in einen Spieler bewegt wird.
588 call_out(#'check_content,1);
589
590 return(ob);
591}
592
593// Schauen, ob die truhe ein Objekt mit passender ID enthaelt. Wenn
594// ja, das Objekt erzeugen und zurueckliefern.
595// Diese Funktion hat eine Reihe von Nachteilen bzw. Defiziten ggue. der
596// normalerweise in Containern definierten Funktion:
597// - Nur das erste Objekt, auf das id passt. Ich hab erstmal keine Lust,
598// hier mehrere Objekte zu erzeugen. Mal schauen, ob es so geht. Auch waere
599// es ein Problem, wenn viele Objekt die Evalgrenze bzw. nicht alle in den
600// Spieler bewegt werden, nachdem sie erzeugt wurden.
601// - keine komplexen Ausdruecke, nur reine IDs.
602// Ich halte den Aufwand fuer nicht gerechtfertigt.
603object *present_objects( string complex_desc ) {
604 object ob;
605 if (!stringp(complex_desc) || !sizeof(complex_desc))
606 return ({});
607 // diese Funktion liefert nur Objete zurueck, wenn der richtige Interactive
608 // versucht, sie zu entnehmen. ;-)
609 if (!objectp(this_interactive()) ||
610 getuuid(this_interactive())!=uuid)
611 return ({});
612
613 // "alles" liefert das erstbeste Objekt.
614 if (complex_desc=="alles" && sizeof(autoloader))
615 {
616 string oname=m_indices(autoloader)[0];
617 ob=create_object(oname);
618 if (objectp(ob)) return ({ob});
619 }
620
621 // ueber alle Eintraege gehen, bis eine ID stimmt, erstes passendes Objekt
622 // erzeugen und in einem Array zurueckliefern.
623 foreach(string oname, string *ids: autoloader) {
624 if (member(ids,complex_desc)==-1) continue;
625 ob=create_object(oname);
626 break; //objekt gefunden, fertig hier
627 }
628 if (objectp(ob)) return ({ob});
629 return ({}); // nix gefunden
630}
631
632
633
634// ******************* Verwaltung *********************************
635
636// registriert die Truhe auf den jeweiligen Eigentuemer.
637protected void SetBesitzer(string unused, string newuuid) {
638 if (!stringp(newuuid) || !sizeof(newuuid)) return;
639 // wenn schon registriert, abbrechen
640 if (stringp(uuid) && sizeof(uuid)) return;
641
642 uuid=newuuid;
643 Set(H_FURNITURE,uuid,F_VALUE); //Setmethode umgehen, sonst Rekursion
644 // ab jetzt nur noch von der Truhe selber.
645 Set(H_FURNITURE,SECURED,F_MODE_AS);
646
647 // Daten fuer den Benutzer aus der Blueprint holen (BP liefert KEINE Kopie
648 // und es darf KEINE gemacht werden!):
649 autoloader=(mapping)load_name()->GetData(uuid);
650
651 // keine Daten gekriegt? -> Fehler loggen
652 if (!mappingp(autoloader))
653 {
654 ERRLOG(sprintf("Keine gueltigen Daten vom Truhenmaster (BP) erhalten. "
655 "initialisiere mit leerem Mapping. :-("));
656 raise_error(sprintf(
657 "Keine gueltigen Daten vom Truhenmaster (BP) fuer UUID %s "
658 "erhalten.\n",uuid));
659 }
660}
661
662// Set-Funktion
663string _set_h_furniture(mixed arg) {
664 if (stringp(arg))
665 {
666 SetBesitzer(0,arg); // bricht ab, wenn bereits registriert.
667 }
668 return(uuid);
669}
670
671// Falls das Speichern in der BP nicht klappte, rufen die Clones diese
672// Funktion. Schreiben der Daten in in Logfile zur Restaurieren per Hand.
673private void EmergencyStore(int res) {
674 ERRLOG(sprintf("EmergencyStore() called. Rueckgabewert des "
675 "Truhenmaster (BP) war: %O. Speichere Daten in Logfile. ",res));
676 write_file(__DIR__+"ALTRUHE.NOTFALLDATEN",
677 sprintf("Daten fuer %O:\n%O\n",uuid,autoloader));
678}
679
680protected void save_me() {
681 int res;
682 // nur BP speichern
683 if (!clonep(ME))
684 save_object(SAVEFILE);
685 else
686 {
687 if (objectp(blueprint(ME)))
688 res=(int)blueprint(ME)->StoreData(uuid,autoloader);
689 else
690 res=(int)load_object(load_name(ME))->StoreData(uuid,autoloader);
691
692 if (res!=1)
693 EmergencyStore(res); // Daten in einem Notfall-Logfile ablegen.
694 }
695}
696
697
698// diese Funktion wird vom Seherhausraum gerufen, sobald ein rebootfestes
699// Moebelstueck erzeugt und konfiguriert wurde.
700void post_create() {
701 if (!clonep()) return;
702
703 // wenn jetzt kein Env: destructen
704 if (!objectp(environment())) {
705 remove(1);
706 return;
707 }
708 //beim Schrankmaster registrieren
709 SCHRANKMASTER->RegisterCnt(ME, QueryProp("cnt_version_std")
710 +":"+QueryProp("cnt_version_obj"), environment()->QueryOwner(),
711 environment());
712}
713
714// diese Funktion wird vom Schrankmaster gerufen, wenn dieser meint, dass
715// dieses Objekt neu erstellt werden sollte.
716int UpdateMe()
717{
718 if (!clonep())
719 return 0;
720 if (!objectp(environment()))
721 return 1;
722 object ob=clone_object(load_name(ME));
723 if (objectp(ob)) {
724 object oldenv=environment();
725 // UUID uebertragen
726 ob->SetProp(H_FURNITURE, uuid);
727 // hierhier bewegen, dabei werden UUID und Daten von der neuen Truhe meist
728 // automatisch geholt.
729 ob->move(oldenv,M_NOCHECK);
730 // jetzt erst post_create() rufen!
731 ob->post_create();
732 // dieses Objekt rausbewegen (damit das Seherhaus es austraegt).
733 move("/room/void",M_NOCHECK);
734 // Seherhausraum speichern (koennte teuer sein!)
735 oldenv->Save(1);
736 // selbstzerstoeren
737 // __INT_MAX__ bedeutet: nicht speichern, die neue Truhe hat die daten
738 // schon aus der BP abgefragt.
739 remove(__INT_MAX__);
740 }
741 return(1);
742}
743
744// bei Selbstzerstoerung speichern bzw. Daten an die Blueprint uebermitteln
745// und beim Schrankmaster abmelden.
746varargs int remove(int silent) {
747 string uid="";
748
749 // Blueprint speichern im Savefile, Clones uebertragen die Daten hier an die
750 // Blueprint. Clones nur, wenn nicht __INT_MAX__ uebergeben wurde.
751 if (silent!=__INT_MAX__ || !clonep())
752 save_me();
753
754 if (clonep()) {
755 // Clone melden sich beim Schrankmaster ab.
756 if (objectp(environment())) {
757 uid=environment()->QueryOwner();
758 }
759 //beim Schrankmaster deregistrieren
760 SCHRANKMASTER->RemoveCnt(ME,uid);
761 }
762 return(::remove(silent));
763}
764
765// ***************** NUR BLUEPRINTS *********************************
766
767// neuen Autoloader zulassen (nur EM+!)
768varargs int AddAutoloader(string path,string nam) {
769 object ob;
770 if (clonep(ME)) return 0;
771 if (!stringp(path) || !sizeof(path)) return -1;
772 if (!ACCESS) return -2;
773 //if (!ARCH_SECURITY) return -2;
774 if (member(whitelist,path)) return -3;
775 if (catch(ob=load_object(path);publish)
776 || !objectp(ob))
777 return -4;
778 // wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
779 if (!stringp(nam) || !sizeof(nam)) {
780 nam=ob->name(WER,0);
781 if (!stringp(nam) || !sizeof(nam)) return -5;
782 }
783 ITEMLOG(sprintf("%s erlaubt: %s (%s)\n",getuid(this_interactive()),
784 path,nam));
785 whitelist+=([path:capitalize(nam)]);
786 if (member(vorschlaege,path))
787 m_delete(vorschlaege,path);
788 save_me();
789 return(1);
790}
791
792// Autoloader aus Erlaubnisliste entfernen (nur EM+!)
793int RemoveAutoloader(string path) {
794 if (clonep(ME)) return 0;
795 if (!stringp(path) || !sizeof(path)) return -1;
796 if (!ACCESS) return -2;
797 //if (!ARCH_SECURITY) return -2;
798 if (!member(whitelist,path)) return -3;
799 ITEMLOG(sprintf("%s widerruft Erlaubnis: %s (%s)\n",getuid(this_interactive()),
800 path,whitelist[path]));
801 whitelist-=([path]);
802 save_me();
803 return(1);
804}
805
806// erlaubte Autoloader abfragen
807mapping QueryAutoloader() {
808 return(copy(whitelist));
809}
810
811// neuen Autoloader in Blacklist eintragen (nur EM+!)
812varargs int AddBlacklist(string path, string nam) {
813 object ob;
814 if (clonep(ME)) return 0;
815 if (!stringp(path) || !sizeof(path)) return -1;
816 if (!ACCESS) return -2;
817 //if (!ARCH_SECURITY) return -2;
818
819 if (member(blacklist,path)) return -3;
820
821 if (catch(ob=load_object(path);publish)
822 || !objectp(ob))
823 return -4;
824 // wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
825 if (!stringp(nam) || !sizeof(nam)) {
826 nam=ob->name(WER,0);
827 if (!stringp(nam) || !sizeof(nam)) return -5;
828 }
829 // ggf. erlaubten entfernen.
830 if (member(whitelist,path))
831 RemoveAutoloader(path);
832 ITEMLOG(sprintf("%s verbietet: %s (%s)\n",getuid(this_interactive()),
833 path,nam));
834
835 blacklist+=([path:capitalize(nam)]);
836
837 if (member(vorschlaege,path))
838 m_delete(vorschlaege,path);
839
840 save_me();
841 return(1);
842}
843
844// Autoloader aus Blacklist entfernen (nur EM+!)
845int RemoveBlacklist(string path) {
846 if (clonep(ME)) return 0;
847 if (!stringp(path) || !sizeof(path)) return -1;
848 if (!ACCESS) return -2;
849 //if (!ARCH_SECURITY) return -2;
850 if (member(blacklist,path)==-1) return -3;
851 ITEMLOG(sprintf("%s loescht: %s (%s) von Blacklist\n",getuid(this_interactive()),
852 path,blacklist[path]));
853 blacklist-=([path]);
854 save_me();
855 return(1);
856}
857
858// gesperrte Autoloader abfragen
859mapping QueryBlacklist() {
860 return(copy(blacklist));
861}
862
863// vorschlaege abfragen
864varargs mixed QueryVorschlaege(int format) {
865 string res="\n";
866 if (!format) return(copy(vorschlaege));
867
868 foreach(string oname, string nam, string sh: vorschlaege) {
869 res+=sprintf("%.78s:\n %.37s,%.37s\n",oname,nam,sh);
870 }
871
872 if (format==2 && objectp(PL))
873 tell_object(PL,res);
874 else
875 return res;
876 return 0;
877}
878
879// Wird diese funktion in der Blueprint gerufen, liefert sie die Daten fuer
880// eine Truhe mit dieser UUID zurueck. Aber nur dann, wenn die auch von einer
881// truhe abgerufen wird und keinem beliebigen anderen Objekt oder wenn ein EM
882// abfragt.
883mapping GetData(string uid) {
884 if (clonep(ME)) return 0;
885 if (extern_call())
886 {
887 if (!objectp(previous_object())) return 0;
888 if (blueprint(previous_object()) != ME
889 && load_name(previous_object()) != __FILE__[..<3]
890 && !ACCESS)
891 return(0);
892 }
893 if (!stringp(uid)) return ([]);
894
895 if (!member(data, uid))
896 data[uid] = m_allocate(1,3);
897
898 // Absichtlich keine Kopie, damit die Truhen (falls jemand mehrere kauft)
899 // sich alle eine Kopie des Mappings teilen.
900 return data[uid];
901}
902
903// Loest die Speicherung der Daten im Savefile aus, denn irgendwelche Daten
904// haben sich in einem Clone geaendert, welche noch auf die Platte muessen.
905// Diese Funktion speicherte frueher Daten aus den Clones wieder im Mapping
906// des Masters (der Blueprint). Dies ist heute nicht mehr so, weil alle Truhen
907// (eines Benutzers) sich data[uid] teilen. Daher wird hier ein Fehler
908// ausgeloest, wenn es von einer Truhe ein Mapping kriegt, welches nicht
909// identisch (!) mit dem data[uid] ist.
910// liefert 0 im Fehlerfall!
911int StoreData(string uid, mapping tmp) {
912
913 if (clonep(ME)) return 0;
914
915 // Aber nur dann, wenn die auch von einer truhe abgerufen wird und keinem
916 // beliebigen anderen Objekt oder wenn ein EM setzt.
917 if (extern_call())
918 {
919 if (!objectp(previous_object()))
920 return 0;
921 if (blueprint(previous_object()) != ME
922 && load_name(previous_object()) != __FILE__[..<3]
923 && !ACCESS)
924 return 0;
925 }
926 if (!stringp(uid) || !mappingp(tmp))
927 return(0);
928
929 // tmp muss auf _dasselbe_ Mapping zeigen wie data[uid]. Wenn das nicht der
930 // Fall ist, ist was schiefgelaufen. Jedenfalls wird dann hier nen Fehler
931 // ausgeloest.
932 if (tmp != data[uid])
933 {
934// if(program_time(previous_object()) < 1400525694)
935// data[uid]=tmp;
936// else
937 raise_error("StoreData() gerufen und Daten sind nicht identisch "
938 "mit den bereits bekannten.\n");
939 }
940
941 // Absichtlich keine Kopie, damit sich alle Truhen das Mapping teilen.
942 //data[uid]=tmp;
943
944 // Savefile muss natuerlich geschrieben werden, fuer den naechsten Reboot.
945 if (find_call_out(#'save_me)==-1)
946 call_out(#'save_me,10); // Speichervorgaenge ggf. sammeln (upd -ar ...)
947 return(1);
948}
949
950// Maintainer ueber Truhenvorschlaege informieren
951void reset() {
952
953 if (clonep() || !sizeof(vorschlaege)) {
954 // ohne Vorschlaege ist auch kein reset noetig.
955 set_next_reset(-1);
956 return;
957 }
958 set_next_reset(12000); // alle 3-4h reicht.
959 foreach(string uid: MAINTAINER) {
960 object pl=find_player(uid);
961 if (objectp(pl) && query_idle(pl) < 1800)
962 tell_object(pl,BS("Es gibt neue Objektvorschlaege fuer die "
963 "Autoloadertruhe. Bitt schau da doch bei Gelegenheit mal drueber."));
964 }
965}
966
967
968// *************************************************************************
969
970// **************** SONSTIGES **********************************************
971
972// *seufz*
973// secure_level() aus der simul_efun ist hier momentan nicht brauchbar, weil
974// dort auch p/seher/moebel/autoloadertruhe in der Callerkette steht und das
975// ein levle von 0 hat. *seufz*
976nomask private int my_secure_level() {
977 int *level;
978 //kette der Caller durchlaufen, den niedrigsten Level in der Kette
979 //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
980 //von 0.
981 //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
982 //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
983 //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
984 //INteractive geben muss.
985 level=map(caller_stack(1),function int (object caller)
986 {if (objectp(caller))
987 return(query_wiz_level(geteuid(caller)));
988 return(0); // kein Objekt da, 0.
989 } );
990 return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
991}
992
993
994// debugkram
995mixed _query_content() {
996 //deep_copy, damit nicht jemand einfach so an den Daten rumbasteln kann.
997 return(deep_copy(autoloader));
998}
999
1000mixed _query_owner() {
1001 return(uuid);
1002}
1003
1004int DeleteData(string uid) {
1005 if (clonep(ME)) return 0;
1006 if (!stringp(uid) || !sizeof(uid)) return -1;
1007 if (!ACCESS) return -2;
1008 //if (!ARCH_SECURITY) return -2;
1009 m_delete(data,uid);
1010 save_me();
1011 return(1);
1012}
1013
1014mixed testfun() {return "bin da\n";}
1015