Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/unit.c b/std/unit.c
new file mode 100644
index 0000000..ae715f2
--- /dev/null
+++ b/std/unit.c
@@ -0,0 +1,771 @@
+// MorgenGrauen MUDlib
+//
+// unit.c -- Basisklasse fuer Mengenobjekte
+// (neue Version von Padreic)
+//
+// $Id: unit.c 9077 2015-01-16 23:26:00Z Zesstra $
+#pragma strong_types,save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing";
+
+#define NEED_PROTOTYPES
+#include <unit.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <thing/properties.h>
+#include <defines.h>
+#include <language.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <debug_info.h>
+
+// zum debuggen und extra public
+public string debugmode;
+public string __set_debug(string recv) {return debugmode=recv;}
+#include <living/comm.h>
+#define ZDEBUG(x) if (stringp(debugmode) && find_player(debugmode)) \
+ find_player(debugmode)->ReceiveMsg(x,MT_DEBUG,0,object_name()+": ",ME)
+//#define ZDEBUG(x)
+
+private void _call_DoDecay(object *klone);
+
+private nosave string lastverb;
+private nosave int lastevalnumber;
+
+protected void create()
+{
+ ::create();
+ SetProp(U_IDS,({({}),({})}));
+ SetProp(P_GENDER, 1);
+ SetProp(P_AMOUNT, 1);
+ // Props sollten nicht direkt manipuliert werden, nur ueber Set-Methoden
+ Set(P_UNIT_DECAY_INTERVAL, PROTECTED, F_MODE_AS);
+ Set(P_UNIT_DECAY_QUOTA, PROTECTED, F_MODE_AS);
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+//// Query und Set Funktionen fuer die Propertys
+
+static void check_leave()
+{ if (Query(P_AMOUNT,F_VALUE)<1) remove(1); }
+
+static int _query_invis()
+{
+ if (QueryProp(P_AMOUNT) < 1)
+ return 1;
+ return Query(P_INVIS, F_VALUE);
+}
+
+static int _set_amount(int am)
+{
+ if (am<1)
+ call_out("check_leave",1);
+ SetProp(U_REQ, am);
+ return Set(P_AMOUNT,am, F_VALUE);
+}
+
+static int _set_u_req(int ureq)
+{
+ lastverb = query_verb();
+ lastevalnumber = debug_info(DINFO_EVAL_NUMBER);
+ return Set(U_REQ, ureq, F_VALUE);
+}
+
+static int _query_u_req()
+{
+ // Zwei Dinge benutzten, um zu entscheiden, ob U_REQ noch gueltig ist oder
+ // besser auf P_AMOUNT zurueckgesetzt werden sollte: die DINFO_EVAL_NUMBER
+ // und das Kommandoverb. Wenn eines von beiden sich geaendert hat, wird
+ // U_REQ als ungueltig betrachtet. (Und dies bleibt ein uebler Hack.)
+ if (lastevalnumber != debug_info(DINFO_EVAL_NUMBER)
+ || lastverb != query_verb())
+ {
+ return SetProp(U_REQ, Query(P_AMOUNT, F_VALUE));
+ }
+ return Query(U_REQ, F_VALUE);
+}
+
+// Gesamtwert der gerade mit U_REQ ausgewaehlten unitobjekte
+static int _query_value()
+{
+ mixed cpu;
+ cpu=Query(U_CPU);
+ if (intp(cpu)) return QueryProp(U_REQ) * cpu;
+ if (!pointerp(cpu)) return 0;
+ return (QueryProp(U_REQ) * cpu[0])/cpu[1];
+}
+
+static int _set_value(int num) {
+// Setzt den Wert fuer die derzeitige Anzahl
+ SetCoinsPerUnits(num, QueryProp(U_REQ));
+ return QueryProp(P_VALUE);
+}
+
+static int _query_weight()
+{
+ mixed gpu, req;
+ if ((req=QueryProp(U_REQ))<1) return 0;
+ gpu=Query(U_GPU);
+ if (intp(gpu)) return req * gpu;
+ if (!pointerp(gpu)) return 0;
+ return MAX(1,(req*gpu[0])/gpu[1]);
+}
+
+static int _set_weight(int num) {
+// Setzt das Gewicht fuer die derzeitige Anzahl
+ SetGramsPerUnits(num, QueryProp(U_REQ));
+ return QueryProp(P_WEIGHT);
+}
+
+static int _query_total_weight()
+{
+ mixed gpu, amount;
+ if ((amount=Query(P_AMOUNT))<1) return 0;
+ gpu=Query(U_GPU);
+ if (intp(gpu))
+ return amount*(int)gpu;
+ if (!pointerp(gpu)) return 0;
+ return MAX(1,(amount*gpu[0])/gpu[1]);
+}
+
+static int _query_plural()
+{
+ int i;
+ i=QueryProp(U_REQ); // wichtig _nicht_ QueryProp
+ return (i<=1 ? 0 : i);
+}
+
+// gibt string | string* zurueck.
+static string|string* _query_name()
+{
+ if (QueryProp(U_REQ)==1)
+ return "%s%s"+((string *)Query(P_NAME))[0];
+ return "%d %s"+((string *)Query(P_NAME))[1];
+}
+
+// gibt string | string* zurueck.
+static string|string* _set_name(mixed names)
+{
+ if(!names)
+ return Set(P_NAME,({"",""}));
+ if(stringp(names))
+ return Set(P_NAME,({names,names}));
+ if(pointerp(names))
+ switch(sizeof(names)) {
+ case 0: return Set(P_NAME,({"",""}));
+ case 1: if(!stringp(names[0]))
+ names[0]="";
+ return Set(P_NAME,({names[0],names[0]}));
+ default: if(!stringp(names[0]))
+ names[0]="";
+ if(!stringp(names[1]))
+ names[1]="";
+ return Set(P_NAME,names[0..1]);
+ }
+ return QueryProp(P_NAME);
+}
+
+// die Funktionen
+
+void AddSingularId(mixed str)
+{
+ string *ids;
+ if (!pointerp(str))
+ str=({str});
+ ids=Query(U_IDS);
+ Set(U_IDS,({ids[0]+str,ids[1]}));
+}
+
+void RemoveSingularId(mixed str)
+{
+ if(stringp(str))
+ str=({str});
+ if(pointerp(str))
+ Set(U_IDS,({Query(U_IDS)[0]-str,Query(U_IDS)[1]}));
+}
+
+void AddPluralId(mixed str)
+{
+ string *ids;
+
+ if (!pointerp(str))
+ str=({str});
+ ids=Query(U_IDS);
+ Set(U_IDS,({ids[0],ids[1]+str}));
+}
+
+void RemovePluralId(mixed str)
+{
+ if(stringp(str))
+ str=({str});
+ if(pointerp(str))
+ Set(U_IDS,({Query(U_IDS)[0],Query(U_IDS)[1]-str}));
+}
+
+void SetCoinsPerUnits(int coins,int units)
+{
+ if (coins<0||units<=0) return;
+ if (units==1)
+ Set(U_CPU, coins);
+ else Set(U_CPU,({coins,units}));
+}
+
+void SetGramsPerUnits(int grams, int units)
+{
+ if (grams<0||units<=0) return;
+ if (units==1)
+ Set(U_GPU, grams);
+ else Set(U_GPU,({grams,units}));
+}
+
+int *QueryCoinsPerUnits()
+{
+ mixed cpu;
+ cpu=Query(U_CPU);
+ if (intp(cpu)) return ({ cpu, 1 });
+ return cpu;
+}
+
+int *QueryGramsPerUnits()
+{
+ mixed gpu;
+ gpu=Query(U_GPU);
+ if (intp(gpu)) return ({ gpu, 1 });
+ return gpu;
+}
+
+void AddAmount(int am) {
+ SetProp(P_AMOUNT, am+Query(P_AMOUNT));
+}
+
+int IsUnit() {
+ return 1;
+}
+
+int IsEqual(object ob)
+// haben ob und dieses Objekte die gleiche Blueprint?
+{
+ if (ob==this_object()) return 0; // das ist ZU gleich :)
+ return (BLUE_NAME(ME)==BLUE_NAME(ob));
+}
+
+// ueberschreiben von Standardmethoden
+
+varargs string name(int fall, int demo)
+{
+ int req;
+ mixed n_adj;
+ string adj;
+
+ if ((req=QueryProp(U_REQ))<1) return 0;
+ if (fall!=RAW &&
+ pointerp(n_adj=QueryProp(P_NAME_ADJ)) && sizeof(n_adj) )
+ adj = implode(map(n_adj, #'DeclAdj, fall, demo && req==1), "");
+ else
+ adj = "";
+
+ if (req==1)
+ return sprintf(QueryProp(P_NAME),
+ (fall==RAW || !QueryProp(P_ARTICLE)
+ ? "" : QueryArticle(fall,demo,1)), adj) /* +korrektur */;
+
+ if (fall!=WEM)
+ return sprintf(QueryProp(P_NAME), req, adj);
+ else {
+ int last;
+ last=Query(P_NAME)[1][<1];
+ return sprintf(QueryProp(P_NAME), req, adj)
+ +(last!='s' && last!='n' ? "n" : "");
+ }
+}
+
+varargs string QueryPronoun(int casus)
+{
+ if (QueryProp(U_REQ)==1)
+ return ::QueryPronoun(casus);
+ switch(casus) {
+ case WESSEN: return "ihrer";
+ case WEM: return "ihnen";
+ //default: return "sie";
+ }
+ return("sie"); //fall-through
+}
+
+string short()
+{
+ if (QueryProp(U_REQ)<1 || QueryProp(P_INVIS)) return 0;
+ return capitalize(name(WER,0))+".\n";
+ /*
+ if (req>1) return sprintf(QueryProp(P_NAME), req)+".\n";
+ return capitalize(sprintf(QueryProp(P_NAME), QueryArticle(WER,0,1)))+".\n";
+ */
+}
+
+varargs string long()
+{
+ return (QueryProp(P_LONG) ? process_string(QueryProp(P_LONG)) : "")+
+ sprintf("Du siehst %s.\n",name(WEN));
+}
+
+varargs int id(string str,int lvl)
+{
+
+ string s1,s2,*ids;
+ int i;
+
+ if (!str) return 0;
+
+ // Wenn kein pos. Amount, hat dieses Objekt keine IDs mehr, damit es nicht
+ // mehr ansprechbar ist.
+ int amount=QueryProp(P_AMOUNT);
+ if (amount < 1)
+ return 0;
+
+ if (::id(str)) {
+ SetProp(U_REQ, Query(P_AMOUNT,F_VALUE));
+ return 1;
+ }
+
+ ids=Query(U_IDS);
+ if (!ids)
+ return 0;
+
+ //die casts auf 'mixed' sind absicht. Sonst geht der Driver offenbar davon
+ //aus, dass ids[1] ein String ist. Es ist aber aber ein Array von Strings
+ //und genau das will auch match_item() haben. ;-) Zesstra
+ if (match_item(str,(mixed)ids[1] )) {
+ SetProp(U_REQ, amount);
+ return 1;
+ }
+ if (match_item(str,(mixed)ids[0] )) {
+ SetProp(U_REQ,1);
+ return 1;
+ }
+ if (sscanf(str,"%s %s",s1,s2) && s1[0..3]=="alle" &&
+ match_item(s2,(mixed)ids[1] )) {
+ SetProp(U_REQ, amount);
+ return 1;
+ }
+ if (sscanf(str,"%d %s",i,s1)==2 && i==1 && match_item(s1,(mixed)ids[0] )) {
+ SetProp(U_REQ,1);
+ return 1;
+ }
+ if (sscanf(str,"%d %s",i,s1)==2 && match_item(s1,(mixed)ids[1] ) &&
+ i<=amount && i>0) {
+ SetProp(U_REQ,i);
+ return 1;
+ }
+ return 0;
+}
+
+// Status fuer move... *seufz*
+int _orig_amount, _move_requested;
+
+varargs int move(object|string dest,int method)
+{
+ _orig_amount = QueryProp(P_AMOUNT);
+ // Wenn M_MOVE_ALL gesetzt ist, wird IMMER ALLES bewegt.
+ if (method & M_MOVE_ALL)
+ _move_requested = _orig_amount;
+ else
+ _move_requested = QueryProp(U_REQ);
+
+ ZDEBUG(sprintf("move from %O to %O: amount %d, req %d\n",environment(),dest,_orig_amount,_move_requested));
+ // Wenn nicht alles bewegt werden soll, wird die Menge dieses Objektes
+ // erstmal reduziert und bewegt. Erst nach der Bewegung wird der Rest dann
+ // im alten Environment neu erzeugt.
+ if ( _orig_amount > _move_requested )
+ {
+ // wenn in einem alten Paket das Gewicht noch nicht neu berechnet
+ // wurde muss dies geschehn solange die Muenzen im Paket noch das
+ // volle Gewicht haben...
+ if ( environment() )
+ environment()->query_weight_contents();
+ // ab jetzt nur auf <_move_requested> Einheiten arbeiten
+ Set( P_AMOUNT, _move_requested, F_VALUE);
+ ZDEBUG(sprintf("move: amount set to %d\n",_move_requested));
+ }
+
+ int res = ::move( dest, method );
+
+ if ( res != MOVE_OK )
+ {
+ // Fehlgeschlagene Bewegung ist doof.
+ // ggf. verfruehte Aenderung (P_AMOUNT oben auf <U_REQ> Einheiten
+ // reduziert) rueckgaengig machen
+ Set( P_AMOUNT, _orig_amount, F_VALUE );
+ ZDEBUG(sprintf("move: not OK, amount restored to %d\n",_orig_amount));
+ return res;
+ }
+
+ // TODO: eigentlich sollte es nicht passieren, dass die Menge jetzt negagtiv
+ // ist. Aber bei Geld kann es durch vereinigen mit Geldboerse/Geldkarten
+ // passieren und das ist kein Fehler. Koennte man evtl. mal differenziert
+ // pruefen.
+ int finalamount= QueryProp(P_AMOUNT);
+ /*if ( finalamount < 1 )
+ {
+ ZDEBUG(sprintf("move: zerstoerung, amount %d\n",finalamount));
+ remove(1);
+ return ME_WAS_DESTRUCTED;
+ }
+ */
+ ZDEBUG(sprintf("move: OK, amount %d\n",finalamount));
+ return res;
+}
+
+
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+ // Erst einen evtl. nicht zu bewegenden Rest im alten Environment wieder erzeugen.
+ if (oldenv && _orig_amount > _move_requested)
+ {
+ if ( oldenv == dest )
+ {
+ // Objekt wurde nur an den Anfang des inventory bewegt, sonst nichts.
+ // ggf. verfruehte Aenderung (oben auf <req> Einheiten reduziert)
+ // rueckgaengig machen
+ ZDEBUG(sprintf("move: same env, amount restored to %d\n",_orig_amount));
+ Set( P_AMOUNT, _orig_amount, F_VALUE );
+ }
+ else
+ {
+ // verbleibende Einheiten in einem neuen Objekte wieder zurueck ins
+ // alte Environment zurueck bewgen
+ object temp;
+ temp = clone_object(BLUE_NAME(ME));
+ temp->SetProp( P_AMOUNT, _orig_amount-_move_requested );
+ temp->move( oldenv, M_NOCHECK );
+ ZDEBUG(sprintf("move: Restmenge im alten Env erzeugt, amount %d\n",_orig_amount-_move_requested));
+ }
+ }
+
+ // Und jetzt ggf. mit den anderen Units gleichen Typs im neuen Environment
+ // vereinigen.
+ foreach(object item: all_inventory(environment()))
+ {
+ if ( IS_EQUAL(item) )
+ {
+ // Es ist ein Feature, dass auch mit Objekten mit negativen Amount
+ // vereinigt wird.
+ ZDEBUG(sprintf("move: vereinige mit %O (%d)\n",
+ item,item->QueryProp(P_AMOUNT)));
+ int mergedamount = QueryProp(P_AMOUNT) + item->QueryProp(P_AMOUNT);
+ item->Set( P_AMOUNT, 0, F_VALUE);
+ item->remove(1);
+
+ SetProp( P_AMOUNT, mergedamount);
+ // U_REQ ist jetzt auch zurueckgesetzt.
+
+ ZDEBUG(sprintf("move: nach vereinigung, amount %d\n",mergedamount));
+ }
+ }
+
+ // wenn man in frisch geclonten Units die noch kein environment haben
+ // per Hand U_REQ auf einen Wert != P_AMOUNT setzt, so gehen die
+ // restlichen Einheiten verloren (es gibt kein Environment, in das man die
+ // Resteinheiten zurueck bewegen koennte).
+
+ // Und jetzt mal Decaykrams machen...
+
+ // wenn NO_DECAY_UNTIL_MOVE an ist und dieses ein Move ist, was von einem
+ // Env in ein anderes Env geht, loeschen. Nicht geloescht wird
+ // hingegen, wenn das move() in das allererste Env erfolgt (nach dem Clonen)
+ if ( (QueryProp(P_UNIT_DECAY_FLAGS) & NO_DECAY_UNTIL_MOVE)
+ && objectp(dest) && objectp(oldenv) && dest != oldenv)
+ SetProp(P_UNIT_DECAY_FLAGS,
+ QueryProp(P_UNIT_DECAY_FLAGS) & ~NO_DECAY_UNTIL_MOVE );
+ ::NotifyMove(dest, oldenv, method);
+}
+
+
+void reset() {
+ if (!clonep(ME)) {
+ // Blueprint ist Master fuer zerfallende Unitobjekte
+ // das Decay sollte besser nicht durch manuellen Aufruf in der BP
+ // ausgeloest werden, daher Pruefung hier (PO == ME, falls der Reset vom
+ // Driver kommt).
+ if (previous_object() && previous_object() != ME)
+ return;
+ int zeit = QueryProp(P_UNIT_DECAY_INTERVAL);
+ if (zeit > 0)
+ {
+ set_next_reset(zeit);
+ // Das Callout muss auf jeden Fall gemacht werden, wenn ein Decay
+ // Intervall gesetzt ist, damit die Blueprint auch den naechsten Reset
+ // kriegt, auch wenn es jetzt keine Clone gibt. Wenn es kein Quota gibt,
+ // kann der Callout wegfallen, beim Setzen eines Quota wird ja eine
+ // Funktion an diesem Objekt gerufen.
+ if (QueryProp(P_UNIT_DECAY_QUOTA) > 0)
+ call_out(#'_call_DoDecay, 1, 0);
+ }
+ }
+ else {
+ // Clones
+ if (Query(P_AMOUNT,F_VALUE)<1) remove(1);
+ else ::reset();
+ }
+}
+
+varargs int remove(int silent)
+{
+ int amount = QueryProp(P_AMOUNT);
+ int req = QueryProp(U_REQ);
+ if (amount > req)
+ {
+ ZDEBUG(sprintf("remove: reduziere amount %d um %d -> %d\n",
+ amount, req, amount-req ));
+ SetProp(P_AMOUNT, amount - req);
+ return 1;
+ }
+ ZDEBUG(sprintf("remove: restlos weg.\n"));
+ return ::remove(silent);
+}
+
+// sollte nicht von aussen gerufen werden.
+private void _call_DoDecay(object *klone) {
+ ZDEBUG(sprintf("call_DoDecay() in %O\n",ME));
+ // Clone oder kein Decay konfiguriert?
+ if (clonep()
+ || QueryProp(P_UNIT_DECAY_FLAGS) & NO_DECAY)
+ return;
+
+ if (!pointerp(klone))
+ klone = clones(2);
+ // Klone da?
+ int i=sizeof(klone);
+ if (!i)
+ return;
+
+ // in allen Klonen DoDecay rufen
+ while(get_eval_cost() > __MAX_EVAL_COST__/2 && i--) {
+ if (objectp(klone[i]))
+ catch(call_other(klone[i],"DoDecay", 0);publish);
+ }
+ // das i ist schon abgearbeitet.
+ i--;
+ // noch was uebrig?
+ if (i >= 0)
+ call_out(#'_call_DoDecay, 4, klone[0..i]);
+}
+
+// zerstoert quota Prozent der vorhandenen Units, liefert die Restmenge
+// zurueck. Ruft ggf. DoDecayMessage()
+public int DoDecay(int silent) {
+ // nur fuer Clones
+ if (!clonep())
+ return 0;
+ ZDEBUG(sprintf("DoDecay() in %O\n",ME));
+
+ // wenn das Objekt NO_DECAY oder NO_DECAY_UNTIL_MOVE in den Flags
+ // zerfaellt nix.
+ int flags = QueryProp(P_UNIT_DECAY_FLAGS);
+ if ((flags & NO_DECAY) || (flags & NO_DECAY_UNTIL_MOVE) )
+ return QueryProp(P_AMOUNT);
+
+ int quota = QueryProp(P_UNIT_DECAY_QUOTA);
+
+ // Zugewinn ist nicht. Ausserdem kann nicht mehr zerfallen, als da ist...
+ // Also ohne gueltiges Quota raus. Nur bei abs. Zerfall sind Zahlen > 10000
+ // erlaubt, bei relativen Zerfall waere das sinnlos.
+ if (quota <= 0 ||
+ (quota > 10000 && !(flags & ABSOLUTE_DECAY) ) )
+ return QueryProp(P_AMOUNT);
+
+ int amount = QueryProp(P_AMOUNT);
+ int minimum = QueryProp(P_UNIT_DECAY_MIN);
+ // Zerfall nur, wenn man mehr als das Minimum hat.
+ if (amount > minimum) {
+ int zerfall;
+ if (flags & ABSOLUTE_DECAY) {
+ // Decay Quota soll einfach als abs. Zahl aufgefasst werden:
+ zerfall = quota;
+ }
+ else if (flags & INACCURATE_DECAY) {
+ // Rundungsfehler ignorieren und dafuer immer min. eine Einheit
+ // zerfallen lassein
+ zerfall = (amount * quota) / 10000;
+ if (zerfall <= 0) zerfall = 1;
+ }
+ else {
+ // Standardfall:
+ // nicht-ganzzahligen Rest als Wahrscheinlichkeit fuer den Zerfall einer
+ // Einheit auffassen
+ float tmp = amount * quota / 10000.0;
+ zerfall = to_int(tmp);
+ tmp -= zerfall; // Rest in tmp
+ // tmp*n ist eine Zahl zwischen 0 und n, wenn tmp*n > random(n)+1,
+ // dann zerfaellt eine Einheit extra. Da das Random fuer grosse Zahlen
+ // besser verteilt ist, nehm ich n = __INT_MAX__
+ if (ceil(tmp * __INT_MAX__) > random(__INT_MAX__) + 1)
+ zerfall++;
+ }
+
+ // nicht unter Minimum zerfallen
+ if (amount - zerfall < minimum)
+ zerfall = amount - minimum;
+
+ // erst ggf. Meldung ausgeben.
+ if (!silent && zerfall > 0)
+ DoDecayMessage(amount, zerfall);
+ // dann runtersetzen.
+ SetProp(P_AMOUNT, amount - zerfall);
+ ZDEBUG(sprintf("%O decayed by %d\n",ME, zerfall));
+ return amount-zerfall;
+ }
+ else if (amount < 0)
+ return 0; // neg. Anzahl gilt als "0 uebrig".
+
+ // sonst ists wohl zwischen 0 und minimum, nix aendert sich.
+ return amount;
+}
+
+// gibt die Zerfallsmeldung aus. Wenn man mit der Standardmeldung nicht zufrieden
+// ist (duerfte der Normalfall sein), sollte man diese Funktion
+// ueberschreiben. Bekommt bisherige und jetzt gerade zerfallene Menge
+// uebergeben. Wichtig: zu diesem Zeitpunkt hat sich an der Unit noch nix (!)
+// geaendert!
+protected void DoDecayMessage(int oldamount, int zerfall) {
+ string msg;
+ if (oldamount == zerfall) {
+ if (living(environment())) {
+ tell_object(environment(), break_string(sprintf(
+ "Auf einmal %s%s zu Staub!",
+ (oldamount>1?"zerfallen Deine ":"zerfaellt D"), name(WER)),78));
+ }
+ // das tell_room() muss auf jeden fall sein, weil es "lebende" Raeume
+ // gibt. Liegt der KRam in einem solchen wuerden die Livings in diesem Raum
+ // sonst keine meldung kriegen.
+ tell_room(environment(), break_string(sprintf(
+ "Auf einmal %s %s zu Staub!",
+ (oldamount>1?"zerfallen":"zerfaellt"), name(WER)),78),
+ ({environment()}));
+ }
+ else {
+ if (living(environment())) {
+ tell_object(environment(), break_string(sprintf(
+ "Auf einmal %s %d Deiner %s zu Staub!",
+ (zerfall>1?"zerfallen":"zerfaellt"),
+ zerfall, Query(P_NAME)[1]),78));
+ }
+ // das tell_room() muss auf jeden fall sein, weil es "lebende" Raeume
+ // gibt. Liegt der KRam in einem solchen wuerden die Livings in diesem Raum
+ // sonst keine meldung kriegen.
+ tell_room(environment(), break_string(sprintf(
+ "Auf einmal %s %d %s zu Staub!",
+ (zerfall>1?"zerfallen":"zerfaellt"),
+ zerfall, Query(P_NAME)[1]),78), ({environment()}) );
+ }
+}
+
+static int _query_unit_decay_interval() {
+ if (clonep()) {
+ // fuer clones den Wert aus der BP liefern, der Wert in Clones ist eh
+ // uninteressant.
+ if (objectp(blueprint()))
+ return blueprint()->QueryProp(P_UNIT_DECAY_INTERVAL);
+ else
+ return load_object(load_name())->QueryProp(P_UNIT_DECAY_INTERVAL);
+ }
+ return Query(P_UNIT_DECAY_INTERVAL);
+}
+
+static int _set_unit_decay_interval(int zeit) {
+ // fuer Clones ist die Prop sinnlos und kann nicht gesetzt werden.
+ if (clonep()) {
+ if (objectp(blueprint()))
+ return blueprint()->QueryProp(P_UNIT_DECAY_INTERVAL);
+ else
+ return load_object(load_name())->QueryProp(P_UNIT_DECAY_INTERVAL);
+ }
+ // ueber diese Prop liesse sich der Reset relativ beliebig veraendern,
+ // verhindern, etc. Daher darf sie nur von diesem Objekt gesetzt werden, von
+ // Weisen+ oder eines Objektes, das die gleiche UID hat wie dieses hier oder
+ // des Programmieres dieses Objekts oder eines Magiers, der berechtigt fuer
+ // die jeweilige UID ist. Letztere beiden werden ueber den Ruf von
+ // QueryUIDsForWizard() ermittelt.
+ // erstes fremdes Objekt finden und pruefen.
+ foreach(object po: caller_stack(1)) {
+ if (po != ME) {
+ // fremden Aufrufer gefunden
+ if (getuid(po) != getuid(ME)
+ && member(master()->QueryUIDsForWizard(getuid(po)),getuid(ME)) == -1
+ && (!this_interactive() || !IS_ELDER(this_interactive()) ) )
+ // unberechtigt, Abbruch
+ return QueryProp(P_UNIT_DECAY_INTERVAL);
+ else
+ // nur das erste ext. Objekt in der Callkette wird geprueft, raus.
+ break;
+ }
+ }
+ set_next_reset(zeit);
+ // falls dies aus dem create() gesetzt wird, muss zeitverzoegert eine
+ // Funktion von aussen am Objekt gerufen werden, da nach einem create() das
+ // Objekt als resettet gilt und sonst keinen Reset kriegt.
+ call_out("_query_unit_decay_interval",1);
+ return Set(P_UNIT_DECAY_INTERVAL, zeit, F_VALUE);
+}
+
+static int _query_unit_decay_quota() {
+ if (clonep()) {
+ // hat dieses Objekt einen eigenen wert gespeichert?
+ int quota=Query(P_UNIT_DECAY_QUOTA, F_VALUE);
+ if (quota)
+ return quota;
+ // sonst den Wert aus der Blueprint
+ if (objectp(blueprint()))
+ return blueprint()->QueryProp(P_UNIT_DECAY_QUOTA);
+ else
+ return load_object(load_name())->QueryProp(P_UNIT_DECAY_QUOTA);
+ }
+ return Query(P_UNIT_DECAY_QUOTA, F_VALUE);
+}
+
+static int _set_unit_decay_quota(int quota) {
+ // momentan nur quota zwischen 0 und 10000
+ if (quota >= 0 && quota <=10000)
+ return Set(P_UNIT_DECAY_QUOTA, quota, F_VALUE);
+ else
+ return Query(P_UNIT_DECAY_QUOTA, F_VALUE);
+}
+
+static int _query_unit_decay_min() {
+ if (clonep()) {
+ // hat dieses Objekt einen eigenen wert gespeichert?
+ int minimum=Query(P_UNIT_DECAY_MIN, F_VALUE);
+ if (minimum)
+ return minimum;
+ // sonst den Wert aus der Blueprint
+ if (objectp(blueprint()))
+ return blueprint()->QueryProp(P_UNIT_DECAY_MIN);
+ else
+ return load_object(load_name())->QueryProp(P_UNIT_DECAY_MIN);
+ }
+ return Query(P_UNIT_DECAY_MIN, F_VALUE);
+}
+
+static int _query_unit_decay_flags() {
+ if (clonep()) {
+ // hat dieses Objekt einen eigenen wert gespeichert?
+ int flags=Query(P_UNIT_DECAY_FLAGS, F_VALUE);
+ if (flags)
+ return flags;
+ // sonst den Wert aus der Blueprint
+ if (objectp(blueprint()))
+ return blueprint()->QueryProp(P_UNIT_DECAY_FLAGS);
+ else
+ return load_object(load_name())->QueryProp(P_UNIT_DECAY_FLAGS);
+ }
+ // Die BP liefert den Wert auf jeden Fall ohne NO_DECAY_UNTIL_MOVE zurueck,
+ // weil dieses Flag im Falle der BP nicht zurueckgesetzt wird und bei Clients
+ // ohne eigene Flags der Zerfall stoppen wuerde.
+ return Query(P_UNIT_DECAY_FLAGS, F_VALUE) & ~NO_DECAY_UNTIL_MOVE;
+}
+