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