blob: 26643d1a923fcefb7dc8185ad1e557cc0ea83c76 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001/*
2 * hausverwalter.c -- Verwaltung der Seherhaeuser
3 *
4 * Das Grundobjekt stammt von Boing, Fortfuehrung erfolgte durch Jof.
5 * Letzte Aenderungen verschuldet durch Wargon ;)
6 *
7 * $Date: 1997/09/09 17:19:29 $
8 * $Revision: 2.3 $
9 * $Log: hausverwalter.c,v $
10 * Revision 2.3 1997/09/09 17:19:29 Wargon
11 * Bugfix beim Verlegen/Loeschen eines Hauses
12 *
13 * Revision 2.2 1996/02/21 18:15:02 Wargon
14 * *** empty log message ***
15 *
16 * Revision 2.0 1994/11/17 13:48:27 Wargon
17 * Modifikationen fuer die Trennung Haus/Raum.
18 *
19 * Revision 1.5 1994/10/24 08:21:55 Wargon
20 * Parameter fuer NeuesHaus geaendert.
21 * Fuer Sicherheitscheck secure() eingebaut.
22 * VerlegeHaus() eingebaut, falls ein Haus mal verlegt werden muss.
23 *
24 * Revision 1.4 1994/10/10 21:50:59 Wargon
25 * NeuesHaus() und LoescheHaus() bedienen nun auch den OBJECTD.
26 * PruefeHaus() wurde damit hinfaellig.
27 *
28 * Revision 1.3 1994/10/09 20:11:48 Wargon
29 * Beschreibung der Haeuser vom Hausverwalter abgekoppelt!
30 * Die megamap enthaelt nur noch Besitzer und Standort des Hauses.
31 * Infolgedessen sind Save() und build() rausgeflogen...
32 *
33 * Revision 1.2 1994/10/07 22:19:48 Wargon
34 * AUFBAU DES MAPPINGS GEAENDERT! Der Filename des Raumes, in dem das
35 * Haus steht, steht jetzt als erster Eintrag im Mapping! Alle anderen
36 * Eintraege sind um 1 weitergewandert.
37 * Beim Laden des Verwalters werden nicht mehr alle Seherhaeuser ge-
38 * laden. Ein Haus wird erst geladen, wenn der Raum, in dem es steht,
39 * geladen wird (PruefeHaus(), siehe auch std/room::create()).
40 *
41 * Revision 1.1 1994/10/07 14:19:36 Wargon
42 * Initial revision
43 *
44 */
45#pragma strict_types
46#include <properties.h>
47#include <wizlevels.h>
48#include <rooms.h>
49#include <moving.h>
50#include "haus.h"
51
52#define H_MAX_ROOMS 9
53
54mapping megamap;
55
56// Haus fuer owner im Raum env erstellen. Wird i.d.R nur vom Instanthaus gemacht.
57void NeuesHaus(string owner, object env);
58
59// Haus von owner loeschen (samt Savefile!). Dieser Vorgang ist unwiderruflich!
60int LoescheHaus(string owner);
61
62// Loescht den letzten hinzugefuegten Raum im Seherhaus von 'owner'.
63void LoescheRaum(string owner);
64
65// Fuegt einen Raum zum Seherhaus von 'owner' hinzu.
66void NeuerRaum(string owner);
67
68// Haus von owner vom Raum 'von' in den Raum 'nach' verschieben.
69int VerlegeHaus(string owner, string von, string nach);
70
71// Kann in ob ein Haus gebaut werden? 0: Ja, sonst Fehler!
72int Unbebaubar(object ob);
73
74// Jemandem im Haus Zusatzrechte einraeumen/entziehen
75string *Erlaube(string owner, string *wer);
76string *Verbiete(string owner, string *wer);
77
78// Eigenschaften aus der megamap abfragen
79mixed HausProp(string owner, int prop);
80
81// Propertymapping deduplizieren
82mixed PCrunch(mapping prop);
83
84// Lade Seherhaus von Besitzer 'owner'
85object _LadeHaus(string owner);
86
87// Lade im Seherhaus von 'owner' den Raum mit der Nummer 'num'
88object _LadeRaum(string owner, int num);
89
90// returnt das Seherhaus von Besitzer 'owner'
91object FindeHaus(string owner);
92
93void create()
94{
95 if (!restore_object(SAVEFILE))
96 megamap = ([ ]);
97 seteuid(getuid(this_object()));
98}
99
100int query_prevent_shadow(object ob)
101{
102 HLOG("SHADOW",sprintf( "%s, von %O im Verwalter.\n",dtime(time())[5..], ob));
103 return 1;
104}
105
106private int
107secure()
108{
109 int ar;
110
111 if (!this_interactive())
112 return 0;
113
114 // das tragbare Instanthaus und die Hausausgabe duerfen:
115 if ((load_name(previous_object()) == PATH+"traghaus") ||
116 (load_name(previous_object()) == PATH+"sb_ausgabe")) {
117 return 1;
118 }
119
120 catch(ar = (int)(PATH+"access_rights")->access_rights(geteuid(this_interactive()), "haus.h"));
121
122 // Erzmagier und alle mit Schreibrechten auf haus.h duerfen
123 if ( (this_interactive() == this_player()) &&
124 (IS_ARCH(this_interactive()) || ar ) )
125 return 1;
126 return 0;
127}
128
129// ersetzt das HAEUSER logfile mit neuer Statistik
130private void
131dump()
132{
133 string *ind;
134 int i, hnum, rnum = 0;
135
136 // loesche logfile
137 rm(PATH+"HAEUSER");
138
139 // betrachte alle Seherhaeuser
140 ind = m_indices(megamap);
141 if (hnum=sizeof(ind)) {
142 write_file(PATH+"HAEUSER", sprintf("Es gibt %d Seherhaeuser:\n", hnum));
143 ind = sort_array(ind,#'>); //'
144 // alle Haeuser sortiert nach Besitzername durchgehen:
145 for(i = 0; i < hnum; ++i) {
146 // zaehle Raeume
147 ++rnum; // Hauptraum
148 rnum += (megamap[ind[i], HP_ROOMS]); // Nebenraeume
149 // Eine Zeile pro Haus: Besitzername (Raumanzahl) Standort-Pfad
150 write_file(PATH+"HAEUSER",
151 sprintf( "%-13s (%d) %s\n",
152 capitalize(ind[i]),
153 megamap[ind[i],HP_ROOMS],
154 megamap[ind[i],HP_ENV] ) );
155 }
156 write_file(PATH+"HAEUSER", sprintf("Es gibt insgesamt %d Raeume.\n", rnum));
157 }
158 else
159 write_file(PATH+"HAEUSER", "KEINE HAEUSER!\n");
160}
161
162// Gegenrichtungen
163#define X_EXIT (["oben":"unten", "unten":"oben",\
164 "westen":"osten", "osten":"westen",\
165 "sueden":"norden", "norden":"sueden",\
166 "nordosten":"suedwesten", "suedwesten":"nordosten",\
167 "nordwesten":"suedosten", "suedosten":"nordwesten" ])
168
169// fuer jeden Raum im Haus [max .. 0] betrachte alle Ausgaenge;
170// zaehle alle Ausgaenge ausser der Haustuer in Raeume,
171// die nicht zu diesem Seherhaus gehoeren
172// (dies sollten Uebergaenge zu anderen Seherhaeusern sein)
173// falls rem != 0 loesche die Gegenrichtung zu diesen Ausgaengen,
174// d.h. kappe alle Uebergaenge aus anderen Seherhaeusern in dieses
175private int
176check_exits(string owner, int max, int rem)
177{
178 int x, nr, bar;
179 string hname, foo;
180 object here, there;
181
182 x = 0;
183 for (nr = max; nr >= 0; --nr) {
184 // betrachte jeden Seherhausraum mit index max .. 0
185 hname = RAUMNAME(owner, nr);
186
187 if (catch(here = load_object(hname);publish)) {
188 printf("error loading %O!\n", hname);
189 continue;
190 }
191 foreach (string dir, string path : (mapping)(here->QueryProp(P_EXITS))) {
192 // betrachte alle Ausgaenge
193 if (dir == "raus") {
194 // Haustuer aus dem Hauptraum darf natuerlich rausfuehren
195 continue;
196 }
197 if ((sscanf(path, PATH+"%sraum%d", foo, bar) != 2) || (foo != owner)) {
198 // Raum in den der Ausgang fuehrt ist nicht in diesem Seherhaus
199 ++x;
200 if (rem) {
201 catch(there = load_object(path);publish);
202 if (there) {
203 // loesche die Gegenrichtung zu dem Ausgang
204 there->RemoveExit(X_EXIT[dir]);
205 there->Save();
206 }
207 }
208 }
209 }
210 }
211 return x;
212}
213
214// Haus fuer owner im Raum env erstellen.
215// Wird i.d.R nur vom Instanthaus gemacht.
216void NeuesHaus(string owner, object env)
217{
218 object h;
219
220 // keine passenden Rechte
221 if (!secure())
222 return;
223
224 // neuen Eintrag im Verwalter-Mapping fuer das Haus erstellen
225 megamap += ([ owner : object_name(env); 0; ({}) ]);
226 // Haus am Bauplatz laden, falls moeglich
227 catch(h = load_object(HAUSNAME(owner));publish);
228 if (!h)
229 return;
230
231 // Haus Speichern und als Raumautoloader eintragen
232 h->Save();
233 OBJECTD->AddObject(h, object_name(env));
234 // Bauplatz auf never clean setzen und Verwalter abspeichern
235 env->SetProp(P_NEVER_CLEAN, 1);
236 save_object(SAVEFILE);
237
238 // Hauptraum des Seherhauses laden
239 h = load_object(RAUMNAME(owner,0));
240 h->SetProp(H_CHEST,1);
241 // Hauptraum speichern
242 h->Save();
243 // Truhe laden
244 h->Load();
245 // Statistik ueber alle Seherhaeuser erneuern
246 dump();
247}
248
249// loescht den letzten hinzufuegten Raum im Seherhaus von 'owner'
250void LoescheRaum(string owner)
251{
252 object raumob;
253 int nr;
254
255 // kein passendes Seherhaus verwaltet oder kein Recht das zu tun
256 if (!member(megamap, owner) || !secure())
257 return;
258
259 nr = megamap[owner, HP_ROOMS];
260 // falls das Haus ueberhaupt zusaetzliche Raeume (neben Hauptraum) hat
261 if (nr > 0 ) {
262 // falls geladen, remove Raum-Objekt
263 raumob = find_object(RAUMNAME(owner,(megamap[owner,HP_ROOMS])));
264 if (objectp(raumob))
265 raumob->remove(1);
266
267 // loesche Raum aus Verwalter-Mapping durch Anzahl um eins erniedrigen
268 --megamap[owner, HP_ROOMS];
269
270 // savefile muss per Hand geloescht werden:
271 tell_object(this_interactive(),
272 break_string(sprintf("Vergiss nicht, das Savefile zu loeschen, "
273 "also: "+HAUSSAVEPATH+"%s%d.o\n",
274 owner, nr),
275 78));
276 // speicher Hausverwaltung ab und erneuer Statistik ueber alle Seherhaeuser
277 save_object(SAVEFILE);
278 dump();
279 }
280}
281
282// Fuegt einen Raum zum Seherhaus von 'owner' hinzu.
283void NeuerRaum(string owner)
284{
285 object raumob;
286
287 // kein passendes Seherhaus verwaltet oder kein Recht das zu tun
288 if (!member(megamap, owner) || !secure())
289 return;
290
291 // ist die Maximalanzahl von Raeumen schon erreicht?
292 if (megamap[owner, HP_ROOMS] < H_MAX_ROOMS)
293 {
294 // erhoehe Raumzaehler in Verwalter-Mapping
295 megamap[owner, HP_ROOMS]++;
296 // lade neuen Raum, falls moeglich
297 catch(raumob = load_object((RAUMNAME(owner,(megamap[owner,
298 HP_ROOMS]))));publish);
299 if(objectp(raumob))
300 // speicher neuen Raum
301 raumob->Save();
302
303 // speicher Verwalter-Mapping und erneuer Statistik ueber alle Seherhaeuser
304 save_object(SAVEFILE);
305 dump();
306 }
307}
308
309// Lade Seherhaus von Besitzer 'owner'
310object _LadeHaus(string owner)
311{
312 object haus;
313 string o;
314
315 // es wird kein passendes Seherhaus verwaltet
316 if (!member(megamap, owner))
317 return 0;
318
319 // Haus ist bereits geladen
320 if (haus=find_object(HAUSNAME(owner)))
321 return haus;
322
323 // lade Bauplatz
324 o = megamap[owner];
325 if (catch(load_object(o);publish))
326 {
327 write_file(PATH+"hauserror", o+" konnte nicht geladen werden.\n");
328 return 0;
329 }
330 // Haus ist durch Laden des Bauplatzes nun geladen
331 if (haus = find_object(HAUSNAME(owner)))
332 return haus;
333
334 // clone Standard-Haus, setze Besitzer
335 haus = clone_object(HAUS);
336 haus->move(o, M_NOCHECK);
337 haus->SetOwner(owner, find_object(RAUMNAME(owner,0)));
338 // lade individualisiertes Haus aus dem Savefile
339 haus->Load();
340
341 return haus;
342}
343
344// Lade im Seherhaus von 'owner' den Raum mit der Nummer 'num'
345object _LadeRaum(string owner, int num)
346{
347 object raum;
348
349 // es wird kein passendes Seherhaus verwaltet
350 if (!member(megamap, owner))
351 return 0;
352
353 // Raumnummer nicht zwischen 0 und letzter Raumnummer
354 if (num < 0 || num > megamap[owner,HP_ROOMS])
355 return 0;
356
357 // Raum ist bereits geladen
358 if (raum = find_object(RAUMNAME(owner,num)))
359 return raum;
360
361 // clone passenden Raum (0: Hauptraum, X: Nebenraum X) und setze Besitzer
362 raum = clone_object(num ? (RAUM) : (PATH+"raum0"));
363 raum->SetOwner(owner, num);
364 // lade Moebel, z.B. Seherhaustruhe
365 raum->Load();
366 // Hauptraum bekommt Haustuer-Ausgang zum Bauplatz
367 if (!num)
368 raum->AddExitNoCheck("raus", megamap[owner]);
369
370 return raum;
371}
372
373// returnt das Seherhaus-Objekt von Besitzer 'ow'
374// nur zum Loeschen oder Wegbewegen daher einfacher als _LadeHaus
375object FindeHaus(string ow)
376{
377 // es wird kein passendes Seherhaus verwaltet
378 if (!member(megamap, ow))
379 return 0;
380 return load_object(HAUSNAME(ow));
381}
382
383// Haus von owner loeschen (samt Savefile!). Dieser Vorgang ist unwiderruflich!
384int LoescheHaus(string owner)
385{
386 object haus;
387 int rooms;
388 string tmp;
389
390 // keine passenden Rechte fuers Loeschen
391 if (!secure())
392 return -1;
393
394 // Haus-Objekt finden, als Raumautoloader im Bauplatz austragen und entfernen
395 haus = FindeHaus(owner);
396 if (!haus)
397 return -2;
398 OBJECTD->RemoveObject(haus, object_name(environment(haus)));
399 environment(haus)->RemoveItem(object_name(haus));
400
401 // Raumanzahl merken
402 rooms = megamap[owner,HP_ROOMS];
403
404 // Haus aus Verwalter-Mapping loeschen
405 megamap = m_delete(megamap, owner);
406
407 // Verwalter-Mapping abspeichern
408 save_object(SAVEFILE);
409
410 // Uebergaenge von anderen Seherhaeusern zu diesem entfernen
411 check_exits(owner, rooms, 1);
412
413 // Savefile fuer das Haus entfernen
414 if (file_size(HAUSSAVEPATH+owner+".o")>0)
415 rm(HAUSSAVEPATH+owner+".o");
416
417 // Savefiles fuer die Raeume entfernen
418 do {
419 if (file_size(tmp = sprintf((HAUSSAVEPATH+"%s%d.o"),owner,rooms))>0)
420 rm(tmp);
421 } while (--rooms >= 0);
422
423 // Savefile fuer die Truhe loeschen
424 if (file_size(HAUSSAVEPATH+owner+"truhe.o")>0)
425 rm(HAUSSAVEPATH+owner+"truhe.o");
426
427 // Repfile fuer das Seherhaus loeschen
428 // TODO: Eintraege aus ERRORD loeschen.
429 if (file_size(PATH+"rep/"+owner+".rep") > 0)
430 rm(PATH+"rep/"+owner+".rep");
431
432 // Statistik ueber alle Seherhaeuser erneuern
433 dump();
434 return 1;
435}
436
437// Haus von owner vom Raum 'von' in den Raum 'nach' verschieben.
438int VerlegeHaus(string owner, string von, string nach)
439{
440 object h, ziel;
441
442 // kein Recht das zu tun
443 if (!secure())
444 return -111;
445
446 // zu verlegendes Haus nicht auffindbar
447 if (!(h=FindeHaus(owner)))
448 return -1;
449
450 // aktueller Standort des Hauses ist nicht Startpunkt von
451 if (object_name(environment(h)) != von)
452 return -2;
453
454 // Ziel-Standort nicht ladbar
455 catch(ziel = load_object(nach);publish);
456 if (!ziel)
457 return -3;
458
459 // Am Zielort darf kein Haus gebaut werden
460 if (Unbebaubar(ziel))
461 return -4;
462
463 // Seherhaus ist mit anderem Seherhaus verbunden und kann daher nicht
464 // verschoben werden
465 if (check_exits(owner, megamap[owner,HP_ROOMS], 0))
466 return -5;
467
468 // neuen Standort in Verwalter-Mapping eintragen
469 megamap[owner] = nach;
470 // Raumautoloader am alten Standort austragen und am neuen eintragen
471 OBJECTD->RemoveObject(h, von);
472 OBJECTD->AddObject(h, nach);
473 // Haus bewegen
474 h->move(nach, M_NOCHECK);
475 // Haustuer-Ausgang umtragen und Hauptraum speichern
476 catch(RAUMNAME(owner,0)->AddExitNoCheck("raus", nach);publish);
477 catch(RAUMNAME(owner,0)->Save();publish);
478 // Verwalter-Mapping speichern
479 save_object(SAVEFILE);
480 // Haus als Inmventar des alten Bauplatzes austragen
481 von->RemoveItem(object_name(h));
482 // Statistik ueber alle Seherhaeuser erneuern
483 dump();
484
485 return 1;
486}
487
488// Kann in ob ein Haus gebaut werden? 0: Ja, sonst Fehler!
489int Unbebaubar(object ob)
490{
491 // Raum ist geclont oder hat kein eigenes Filet, z.B. VC
492 if (clonep(ob) || file_size(object_name(ob)+".c")<0)
493 return 1;
494
495 // Innenraum
496 if (ob->QueryProp(P_INDOORS))
497 return 2;
498
499 // Bauplatz-Property nicht gesetzt
500 if (!(ob->QueryProp(P_HAUS_ERLAUBT)))
501 return 3;
502
503 return 0;
504}
505
506// Jemandem im Haus Zusatzrechte einraeumen
507string *Erlaube(string owner, string *wer)
508{
509 string *all;
510
511 // es wird kein passendes Seherhaus verwaltet
512 if (!member(megamap, owner))
513 return 0;
514
515 all = megamap[owner, HP_ALLOWED];
516 // fuege wer zu all dazu ohne doppelte Eintraege zu erzeugen:
517 all += wer-all;
518 // aender Rechteliste in der Verwaltung
519 megamap[owner, HP_ALLOWED] = all;
520 // speicher Verwalter-Mapping
521 save_object(SAVEFILE);
522 // return Liste der aktuellen Rechteinhaber
523 return all;
524}
525
526// Jemandem im Haus Zusatzrechte entziehen
527string *Verbiete(string owner, string *wer)
528{
529 // es wird kein passendes Seherhaus verwaltet
530 if (!member(megamap, owner))
531 return 0;
532
533 // aender Rechteliste in der Verwaltung
534 megamap[owner, HP_ALLOWED] -= wer;
535 // speicher Verwalter-Mapping
536 save_object(SAVEFILE);
537 // return Liste der aktuellen Rechteinhaber
538 return megamap[owner, HP_ALLOWED];
539}
540
541// Abfrage von Property 'prop' im Haus von 'owner'
542// prop gleich HP_ENV, HP_ROOMS oder HP_ALLOWED aus haus.h
543mixed HausProp(string owner, int prop)
544{
545 // es wird kein passendes Seherhaus verwaltet
546 if (!member(megamap, owner))
547 return 0;
548
549 return megamap[owner,prop];
550}
551
552// zerlegt das mapping, fasst indices mit gleichen values zusammen
553// und packt das ganze in ein array -> Deduplizieren
554// Das Ergebnis enthaelt
555// a) fuer jedes Detail ein Array, welches als erstes Element ein Array von
556// Schluesseln und als zweites Element einen String enthaelt.
557// b) fuer jedes Kommando ein Array, welches als erstes Element ein Array von
558// Syntaxen und zwei folgende Elemente mit Strings (Meldungen) enthaelt.
559// Beides ist in einem Format, dass die Elemente unmodifiziert an die
560// entsprechenden Add...()-Aufrufe gegeben werden koennen.
561
562// ([ "key1" : A, "key2" : B, "key3" : A ])
563// => ({ ({ ({ "key1", "key3" }), A )}, ({ ({ "key2" }), B )} })
564
565// ([ "key1" : ([ "key11" : A; B, "key12" : C; B, "key13" : A, B ]),
566// "key2" : ([ "key21" : A; B, "key22" : C; A ]) ])
567// => ({ ({ ({ "key1 key11", "key1 key13", "key2 key21" }), A, B }),
568// ({ ({ "key1 key12" }), C, B }),
569// ({ ({ "key2 key22" }), C, A }) })
570mixed PCrunch(mapping prop)
571{
572 mixed ret = ({});
573 int done = 0;
574 foreach(string key, mixed entry : prop)
575 {
576 if(mappingp(entry))
577 {
578 // mapping breite 2 im mapping fuer H_COMMANDS
579 foreach(string subkey, string val1, string val2 : entry)
580 {
581 done = 0;
582 foreach(mixed retset : ret)
583 {
584 // falls es schon im ergebnis-array etwas mit den selben werten gibt,
585 // fuege den index dort dazu
586 if(sizeof(retset) == 3 && retset[1] == val1 && retset[2] == val2)
587 {
588 retset[0] += ({ key+" "+subkey });
589 done = 1;
590 break;
591 }
592 }
593 // falls es noch nix im ergebnis-array mit den selben werten gab,
594 // fuege einen neuen eintrag hinzu
595 if(!done)
596 ret += ({ ({ ({ key+" "+subkey }), val1, val2 }) });
597 }
598 }
599 else
600 {
601 // einzelne Werte im Mapping fuer P_DETAILS und P_READ_DETAILS
602 done = 0;
603 foreach(mixed retset : ret)
604 {
605 // falls es schon im ergebnis-array etwas mit dem selben wert gibt,
606 // fuege den index dort dazu
607 if(sizeof(retset) == 2 && retset[1] == entry)
608 {
609 retset[0] += ({ key });
610 done = 1;
611 break;
612 }
613 }
614 // falls es noch nix im ergebnis-array mit dem selben wert gab,
615 // fuege einen neuen eintrag hinzu
616 if(!done)
617 ret += ({ ({ ({ key }), entry }) });
618 }
619 }
620 return ret;
621}