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