Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/thing/description.c b/std/thing/description.c
new file mode 100644
index 0000000..47bfb0e
--- /dev/null
+++ b/std/thing/description.c
@@ -0,0 +1,851 @@
+// MorgenGrauen MUDlib
+//
+// thing/description.c -- description handling for standard objects
+//
+// $Id: description.c 9561 2016-05-25 19:33:22Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <tls.h>
+#include <thing/description.h>
+#include <thing/material.h>
+#include <thing/lighttypes.h>
+#include <exploration.h> // wegen EPMASTER
+#include <class.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/language.h>
+
+#undef NEED_PROTOTYPES
+#include <properties.h>
+
+// Variable um den FP abzuspeichern
+private nosave mixed *explore;
+
+// Prototypen
+public string short();
+public varargs string long(int mode);
+
+// #####################
+//######################## System-Funktionen ############################
+// #####################
+
+// Objekt erzeugen
+protected void create()
+{
+ string poid, tpid;
+ object tp;
+
+ SetProp( P_NAME, "Ding" );
+ SetProp( P_SHORT, "Nichts besonderes" );
+ SetProp( P_LONG, 0 );
+
+ Set( P_ADJECTIVES, ({}) );
+ Set( P_NAME_ADJ, ({}) );
+ Set( P_IDS, ({}) );
+ Set( P_CLASS, ({}) );
+
+ Set( P_READ_DETAILS, ([]), F_VALUE);
+ Set( P_READ_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS);
+
+ Set( P_DETAILS, ([]), F_VALUE);
+ Set( P_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+ Set( P_SMELLS, ([]), F_VALUE);
+ Set( P_SMELLS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+ Set( P_SOUNDS, ([]), F_VALUE);
+ Set( P_SOUNDS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+ Set( P_TOUCH_DETAILS, ([]), F_VALUE);
+ Set( P_TOUCH_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+ // Aenderungen an dieser Prop sind tabu.
+ Set( P_CLONE_TIME, NOSETMETHOD|SECURED, F_MODE_AS );
+
+ // Id des Cloners und des Besitzers kommen nach P_CLONER
+ if (objectp( tp=this_interactive()||this_player() ))
+ {
+ tpid=geteuid(tp);
+ if (!(tpid=geteuid(tp))) tpid=getuid(tp);
+ }
+ else
+ tpid="UNKNOWN";
+
+ if (previous_object())
+ {
+ if (!(poid = geteuid(previous_object())))
+ poid = getuid(previous_object());
+ }
+ else
+ poid="UNKNOWN";
+
+ Set( P_CLONER, (poid != tpid ? poid+":"+tpid: tpid) );
+ Set( P_CLONER, NOSETMETHOD|SECURED, F_MODE_AS );
+
+ // Gibt es FPs ?
+ explore = (mixed *)EPMASTER->QueryExplore();
+
+ return;
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+// ##################
+//######################### Forscherpunkte ##############################
+// ##################
+
+// FP vergeben
+static void GiveEP( int type, string key )
+{
+ //Abbruch, wenn vergebendes Objekt schon zerstoert ist. ACHTUNG: Auch
+ //diese Abfrage wuerde kein FP vergeben werden, wenn sich das Objekt im
+ //vergebenden Kommando zerstoert, da der Driver call_other() von zerstoerten
+ //Objekten ignoriert!
+ if (!objectp(this_object())) return;
+ if (this_player()) this_player()->countCmds( type, key );
+
+ if (explore&&!extern_call()&&
+ (explore[0] == type) && (member(explore[1], key) >= 0) )
+ EPMASTER->GiveExplorationPoint(key);
+ return;
+}
+
+// Manche Objekte koennen mit rename_object einen neuen Filenamen bekommen.
+// Danach sollte der EPMASTER neu nach den Details befragt werden.
+void __reload_explore()
+{
+ explore = (mixed *)EPMASTER->QueryExplore();
+ return;
+}
+
+// #################
+//########################## ID-Management ##############################
+// #################
+
+// Gibt eine ID zurueck, die den Ladenamen, die aktuelle Kurzbeschreibung und
+// die aktuelle Langbeschreibung beruecksichtigt. Diese ID ist ein Hashwert.
+public string description_id() {
+ return hash(TLS_HASH_MD5, load_name() + short() + long());
+}
+
+/* Ids muessen uebergeben werden, da unit machmal mit plural-Ids, */
+/* also anderen als den normalen arbeiten muss. */
+int match_item( string str, string *ids )
+{
+ string *obj,*ads;
+ int len, i;
+
+ // Parameter falsch? Passt nicht ...
+ if(!str) return 0;
+ if(!pointerp(ids)) return 0;
+ if(!sizeof(ids)) return 0;
+
+ // Ist schon so dabei? Na Klasse :-)
+ if(member(ids,str)>-1) return 1;
+
+ // Keine Adjektive vorhanden ... passt nicht.
+ if (!(ads=QueryProp(P_ADJECTIVES))) return 0;
+ if (!sizeof(ads)) return 0;
+
+ // Nur ein Wort? Dann passt es nicht
+ obj=explode(str," ");
+ if (!(len=sizeof(obj)-1)) return 0;
+
+ // Adjektive stehen am Anfang. Sobald es nicht mehr passt,
+ // muss es das Objektiv sein.
+ while(i<len&&member(ads,obj[i])>-1) i++;
+
+ return (member(ids,implode(obj[i..len]," "))>-1);
+}
+
+// Wird vom Gamedriver aufgerufen (present)
+// Hat dieser Gegenstand die ID str?
+// lvl wird ignoriert
+varargs int id( string str, int lvl )
+{
+ string str2, tmp;
+ int count;
+ string|string* ids;
+
+ // Kein Argument? Dann passt es nicht ...
+ if (!stringp(str)) return 0;
+
+ // Keine IDs? Auch nicht gut ...
+ if (!pointerp(ids=QueryProp(P_IDS))) return 0;
+ if (!sizeof(ids)) return 0;
+
+ ids += ({ ("\n" + object_name()),
+ ("\n" + explode(object_name(),"#")[0]) });
+
+ // Id passt? Alles klar :-)
+ if (match_item( str, ids )) return 1;
+
+ // Die id hat eine Zahl drin. Wenn Zahl die Rohid passt,
+ // dann gucken, ob man selber das nte Element ist.
+ if (sscanf( str, "%s %d%s", str2, count, tmp)<2) return 0;
+ if (count<1) return 0;
+ if (sizeof(tmp)) return 0;
+ if (!match_item( str2, ids )) return 0;
+ if (!environment()) return 0;
+ return present(str2,count,environment())==this_object();
+}
+
+// Gleich eine ganze Liste von ids testen
+int match_ids(string *list)
+{
+ string *ids;
+
+ // Ungueltige Parameter? Weg hier ...
+ if (!pointerp(list)) return 0;
+ if (!pointerp(ids=QueryProp(P_IDS))) return 0;
+
+ ids += ({ ("\n" + object_name()),
+ ("\n" + explode(object_name(),"#")[0]) });
+
+ return sizeof( list & ids );
+}
+
+// ID hinzufuegen
+void AddId( string|string* str )
+{
+ if (stringp(str)) str = ({ str });
+ if (pointerp(str))
+ // Doppelte eliminieren
+ Set( P_IDS, Query(P_IDS, F_VALUE)-str+str, F_VALUE);
+ return;
+}
+
+// ID entfernen
+void RemoveId(string|string* str)
+{
+ if (stringp(str)) str = ({ str });
+ if (pointerp(str))
+ Set(P_IDS,Query(P_IDS, F_VALUE)-str, F_VALUE);
+ return;
+}
+
+// Alle Ids auf einmal setzen
+static string* _set_ids( string* ids )
+{
+ Set( P_IDS,({}));
+ AddId(ids);
+ return Query(P_IDS, F_VALUE);
+}
+
+// Adjektiv hinzufuegen
+void AddAdjective(string|string* str)
+{
+ if (stringp(str)) str = ({ str });
+ if (pointerp(str))
+ // Doppelte eliminieren
+ Set( P_ADJECTIVES, Query(P_ADJECTIVES, F_VALUE)-str+str, F_VALUE );
+ return;
+}
+
+// Adjektiv entfernen
+void RemoveAdjective(string|string* str)
+{
+ if (stringp(str)) str = ({ str });
+ if (pointerp(str))
+ Set( P_ADJECTIVES, Query(P_ADJECTIVES, F_VALUE) - str, F_VALUE );
+ return;
+}
+
+// Alle Adjektive auf einmal setzen
+static string* _set_adjectives(string* adjectives)
+{
+ Set( P_ADJECTIVES,({}), F_VALUE);
+ AddAdjective(adjectives);
+ return Query(P_ADJECTIVES, F_VALUE);
+}
+
+
+// ################
+//########################## Namensgebung ###############################
+// ################
+
+// Im Fall von mehreren Adjektiven muessen diese mit komma
+// zusamengebaut werden, dazu muss ich das leerzeichen aber erstmal
+// abschneiden und so weiter ...
+private string depointer_adj( string* adj, int casus, int demon ) {
+ string msg;
+ int start;
+ string res,a;
+ adj = map( adj, #'DeclAdj, casus, demon );
+ start = 1;
+ res = "";
+ foreach( a: adj ) {
+ res += (start ? "" : ", ") + a[0..<2];
+ start = 0;
+ }
+ return res + " ";
+}
+
+// Wie lautet der Name des Objekts?
+varargs string name(int casus,int demon)
+{
+ mixed sh, adj;
+ int art, plural;
+
+ art = QueryProp(P_ARTICLE);
+
+ // RAW: direkt zurueckgeben ohne Verarbeitung
+ if (casus == RAW )
+ {
+ if(pointerp(QueryProp(P_NAME)))
+ return QueryProp(P_NAME)[WER];
+ return QueryProp(P_NAME);
+ }
+
+ // Unsichtbar: Etwas
+ if (QueryProp(P_INVIS))
+ return ({ "etwas", "von etwas", "etwas", "etwas" })[casus];
+
+ // Kein Name? Schade ...
+ if (!(sh=QueryProp(P_NAME)) ||
+ (stringp(sh) && !sizeof(sh))) return 0;
+
+ // P_NAME pruefen.
+ if (pointerp(sh) && sizeof(sh) != 4)
+ raise_error(sprintf("Ungueltige Arraygroesse in P_NAME: %d\n",
+ sizeof(sh)));
+
+ // Plural .. Namen verwursten
+ if (plural = QueryProp(P_PLURAL))
+ {
+ // Selber Artikel suchen ist nicht ...
+ if (demon==2||!art) demon = 0;
+
+ // falls P_NAME ein Array mit Faellen enthaelt, den richtigen
+ // extrahieren...
+ if (pointerp(sh)) {
+ sh = sh[casus];
+ }
+ else {
+ // sonst versuchen, zu deklinieren.
+ int last = sh[<1];
+ if (casus == WEM&&last!='s'&&last!='n') sh = sh + "n";
+ }
+
+ // Sind Adjektive vorhanden?
+ if ( pointerp(adj = QueryProp(P_NAME_ADJ)) && sizeof(adj))
+ adj = depointer_adj(adj,casus,demon);
+ if (!stringp(adj)) adj = "";
+
+ return sprintf("%s%s%s%s",QueryArticle(casus,demon,0),adj,
+ (plural < 2 ? "":(plural < 8 ?
+ ({ "zwei ", "drei ", "vier ", "fuenf ", "sechs ",
+ "sieben " })[plural-2] : to_string(plural)+" ")),sh);
+ }
+
+ // Name ist Pointer: Einfach den richtigen auswaehlen
+ if (pointerp(sh))
+ sh = sh[casus];
+
+ // Ansonsten doch wieder verwursten ...
+ else if (stringp(sh))
+ {
+ int last = sh[<1];
+
+ switch(casus)
+ {
+ case WEM:
+ case WEN:
+ if ( art && last=='e'&&QueryProp(P_GENDER) == MALE)
+ sh = (string)sh + "n";
+ break;
+
+ case WESSEN:
+ if( !art )
+ {
+ switch(last)
+ {
+ case 'x':
+ case 's':
+ case 'z':
+ sh = (string)sh + "'";
+ break;
+
+ default:
+ sh = (string)sh + "s";
+ }
+ }
+ else
+ {
+ switch(last)
+ {
+ default:
+ if (QueryProp(P_GENDER)!=FEMALE)
+ sh=(string)sh+"s";
+ break;
+ case 'e':
+ if (QueryProp(P_GENDER)==MALE)
+ sh=(string)sh+"n";
+ case 'x':
+ case 's':
+ case 'z':
+ break;
+ } /* switch (last) */
+ } /* if( !art ) else */
+ } /* switch( casus ) */
+ } /* pointerp(sh) */
+
+ // RAW? Dann mal zurueck
+ if (demon == RAW) return (string)sh;
+
+ // Selber Artikel suchen ...
+ if (demon==2)
+ {
+ if (art)
+ demon = SuggestArticle();
+ else
+ demon=0; // Kein Artikel: egal (SuggestArticle ist zeitaufwendig)
+ }
+
+ if (pointerp(adj = QueryProp(P_NAME_ADJ)) && sizeof(adj))
+ adj = depointer_adj(adj,casus,demon);
+
+ if (!stringp(adj)) adj = "";
+
+ return QueryArticle( casus, demon )+adj+sh;
+}
+
+// Grossgeschriebenen Namen zurueckgeben
+varargs string Name( int casus, int demon )
+{
+ return capitalize(name( casus, demon )||"");
+}
+
+// Langbeschreibung anzeigen
+public varargs string long(int mode)
+{
+ return process_string( QueryProp(P_LONG) );
+}
+
+// Kurzbeschreibung anzeigen, falls nicht unsichtbar
+public string short()
+{
+ string sh;
+
+ // Unsichtbar? Dann gibts nichts zu sehen ...
+ if (QueryProp(P_INVIS)||!(sh=QueryProp(P_SHORT)))
+ return 0;
+
+ return process_string(sh)+".\n";
+}
+
+// Namens-Adjektive setzen
+static string* _set_name_adj(string|string* adjectives)
+{
+ if (!adjectives)
+ adjectives=({});
+ // In Array umwandeln
+ else if ( !pointerp(adjectives))
+ adjectives = ({ to_string(adjectives) });
+ return Set( P_NAME_ADJ, adjectives );
+}
+
+// ############################
+//#################### Details, Gerueche, Laerm #########################
+// ############################
+
+// Low-level Funktion zum Ergaenzen von Details, wird von den div. Add...()
+// gerufen, die das richtige Mapping uebergeben.
+// Aendert das Mapping <details> direkt.
+private void _add_details(string|string* keys,
+ string|string*|mapping|closure descr,
+ mapping details )
+{
+ if (stringp(keys))
+ details[keys]=descr;
+ else if (pointerp(keys))
+ {
+ foreach(string key : keys)
+ details[lower_case(key)]=descr;
+ }
+ else
+ raise_error("Wrong type to argument 1, expected string|string*.\n");
+}
+
+// Low-level Funktion zum Entfernen von Details, wird von den div. Remove...()
+// gerufen, die das richtige Mapping uebergeben.
+// Aendert das Mapping <details> direkt.
+private void _remove_details(string|string* keys, mapping details )
+{
+ if (stringp(keys))
+ details -= ([keys]);
+ else if (pointerp(keys))
+ details -= mkmapping(keys);
+ else
+ raise_error("Wrong type to argument 1, expected string|string*.\n");
+}
+
+// Detail(s) hinzufuegen
+void AddDetail(string|string* keys, string|string*|mapping|closure descr)
+{
+ int i;
+ mapping details;
+
+ details = Query(P_DETAILS, F_VALUE);
+
+ // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+ return _add_details(keys, descr, details);
+}
+
+// Detail(s) entfernen
+varargs void RemoveDetail(string|string* keys )
+{
+ // Alle loeschen geht direkt ...
+ if (!keys )
+ Set(P_DETAILS, ([]), F_VALUE);
+ else
+ // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+ _remove_details(keys, Query(P_DETAILS, F_VALUE));
+}
+
+// SpecialDetail hinzufuegen
+void AddSpecialDetail(string|string* keys, string functionname )
+{
+ closure cl;
+
+ // Absichern! Sonst koennte jeder interne Funktionen aufrufen
+ if (extern_call() &&
+ (geteuid(previous_object()) != geteuid() || process_call()) &&
+ !(object_name(previous_object()) == "/obj/doormaster" &&
+ functionname == "special_detail_doors") )
+ raise_error( "Illegal use of AddSpecialDetail!\n" );
+
+ // Closure generieren
+ if ( !stringp(functionname)||
+ !(cl = symbol_function( functionname, this_object())) )
+ return;
+
+ // Detail hinzufuegen
+ AddDetail( keys, cl );
+ return;
+}
+
+// SpecialDetail(s) entfernen
+void RemoveSpecialDetail(string|string* keys )
+{
+ // RemoveSpecialDetail(0) wuerde sonst ALLE Details (auch die
+ // 'normalen') loeschen
+ if (pointerp(keys)||stringp(keys))
+ RemoveDetail(keys);
+ return;
+}
+
+// Lesbares Detail einfuegen
+void AddReadDetail(string|string* keys,
+ string|string*|mapping|closure descr )
+{
+ // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+ return _add_details(keys, descr, Query(P_READ_DETAILS, F_VALUE));
+}
+
+// Lesbare(s) Detail(s) entfernen
+varargs void RemoveReadDetail(string|string* keys )
+{
+ // Alle loeschen geht direkt ...
+ if (!keys )
+ Set(P_READ_DETAILS, ([]), F_VALUE);
+ else
+ // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+ _remove_details(keys, Query(P_READ_DETAILS, F_VALUE));
+}
+
+// Geraeusch(e) dazufuegen
+void AddSounds(string|string* keys,
+ string|string*|mapping|closure descr )
+{
+ // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+ return _add_details(keys, descr, Query(P_SOUNDS, F_VALUE));
+}
+
+// Geraeusch(e) entfernen
+varargs void RemoveSounds(string|string* keys )
+{
+ // Alle loeschen geht direkt ...
+ if (!keys )
+ Set(P_SOUNDS, ([]), F_VALUE);
+ else
+ // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+ _remove_details(keys, Query(P_SOUNDS, F_VALUE));
+}
+
+// Geru(e)ch(e) hinzufuegen
+void AddSmells(string|string* keys,
+ string|string*|mapping|closure descr )
+{
+ // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+ return _add_details(keys, descr, Query(P_SMELLS, F_VALUE));
+}
+
+// Geru(e)ch(e) entfernen
+varargs void RemoveSmells(string|string* keys )
+{
+ // Alle loeschen geht direkt ...
+ if (!keys )
+ Set(P_SMELLS, ([]), F_VALUE);
+ else
+ // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+ _remove_details(keys, Query(P_SMELLS, F_VALUE));
+}
+
+// Tastbare(s) Detail(s) hinzufuegen
+void AddTouchDetail(string|string* keys,
+ string|string*|mapping|closure descr )
+{
+ // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+ return _add_details(keys, descr, Query(P_TOUCH_DETAILS, F_VALUE));
+}
+
+// Tastbare(s) Detail(s) entfernen
+varargs void RemoveTouchDetails(string|string* keys )
+{
+ // Alle loeschen geht direkt ...
+ if (!keys )
+ Set(P_TOUCH_DETAILS, ([]), F_VALUE);
+ else
+ // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+ _remove_details(keys, Query(P_TOUCH_DETAILS, F_VALUE));
+}
+
+// Detailinfos fuer Detail key, Spieler hat die Rasse race
+// und benutzt seinen Sinn sense
+varargs string GetDetail(string key, string race, int sense)
+{
+ string|string*|mapping|closure detail;
+
+ if (stringp(race)) race = lower_case(race);
+
+ switch(sense)
+ {
+ case SENSE_SMELL: detail=Query(P_SMELLS, F_VALUE)[key];
+ sense=EP_SMELL; break;
+ case SENSE_SOUND: detail=Query(P_SOUNDS, F_VALUE)[key];
+ sense=EP_SOUND; break;
+ case SENSE_TOUCH: detail=Query(P_TOUCH_DETAILS, F_VALUE)[key];
+ sense=EP_TOUCH; break;
+ case SENSE_READ: detail=Query(P_READ_DETAILS, F_VALUE)[key];
+ sense=EP_RDET;
+ break;
+
+ default: detail=Query(P_DETAILS, F_VALUE)[key];
+ sense=EP_DETAIL; break;
+ }
+
+ if (!stringp(detail))
+ {
+ if (closurep(detail))
+ detail = (string)funcall(detail,key);
+ else if (mappingp(detail))
+ detail = (string)(detail[race]||detail[0]);
+ else if (pointerp(detail))
+ detail = (string)(detail[random(sizeof(detail))]);
+ }
+
+ // FP vergeben (so vorhanden ;-) )
+ if (detail) GiveEP(sense,key);
+
+ return detail;
+}
+
+// TODO: OBSOLET (Libgrep notwendig)
+void read( string str ) {
+ raise_error("Diese Funktion existiert nicht mehr.\n");
+}
+
+
+// ######################
+//####################### Zugriffsfunktionen ############################
+// ######################
+
+// Dienen dazu, die direkte Manipulation der Props von aussen zu erschweren.
+
+// Filter, um Specialdetails zu eliminieren
+// erstellt ausserdem ne Kopie vom Mapping. (Wichtig!)
+// Wird vor allem benoetigt, um P_DETAILS in P_DETAILS und
+// P_SPECIAL_DETAILS zu treffen.
+private int _closures(string x, mapping details, int yes )
+{
+ return yes ? closurep(details[x]) : !closurep(details[x]);
+}
+
+static mapping _query_details()
+{
+ return filter_indices(Query(P_DETAILS, F_VALUE), #'_closures,
+ Query(P_DETAILS, F_VALUE),0);
+}
+
+static mapping _query_special_details()
+{
+ return filter_indices(Query(P_DETAILS, F_VALUE),#'_closures,
+ Query(P_DETAILS, F_VALUE),1);
+}
+
+static mapping _query_read_details() {
+ return deep_copy(Query(P_READ_DETAILS, F_VALUE));
+}
+
+static mapping _query_sound_details() {
+ return deep_copy(Query(P_SOUNDS, F_VALUE));
+}
+
+static mapping _query_smell_details() {
+ return deep_copy(Query(P_SMELLS, F_VALUE));
+}
+
+
+// ##########################
+//##################### Klassen-Mitgliedschaft ##########################
+// ##########################
+
+// Klasse hinzufuegen
+public void AddClass(string|string* str)
+{
+ if (stringp(str))
+ str = ({ str });
+ // Aliase aufloesen und implizite Klassen addieren.
+ str = (string*)CLASSDB->AddImplicitClasses(str);
+ // Summe mit alten Klassen bilden und Doppelte eliminieren
+ str = str + Query(P_CLASS, F_VALUE);
+ Set( P_CLASS, m_indices(mkmapping(str)), F_VALUE);
+
+ return;
+}
+
+// Klasse entfernen
+void RemoveClass(string|string* str)
+{
+ if (stringp(str))
+ str = ({ str });
+
+ // Aliase aufloesen und implizite Klassen addieren.
+ str = (string*)CLASSDB->AddImplicitClasses(str);
+
+ // Und alle - inklusive impliziter Klassen - entfernen
+ // TODO: Pruefen, ob dies die richtige Entscheidung ist.
+ Set( P_CLASS, Query(P_CLASS, F_VALUE)-str, F_VALUE);
+
+ return;
+}
+
+// Ist das Objekt Mitglied der Klasse str?
+int is_class_member(string|string* str)
+{
+ // Keine Klasse, keine Mitgliedschaft ...
+ if (!str || str=="")
+ return 0;
+
+ // Es sollte schon ein Array sein
+ if (stringp(str))
+ str = ({ str });
+
+ // Klassen und Ids ins Array
+ string *classes=QueryProp(P_CLASS);
+ if (!pointerp(classes))
+ return 0;
+
+ // .. und testen
+ foreach(string class : str)
+ if (member(classes,class) > -1 ) return 1;
+
+ return 0;
+}
+
+// Klasse direkt setzen abfangen
+static string* _set_class(string* classes )
+{
+ Set( P_CLASS, ({}), F_VALUE );
+ AddClass(classes);
+ return QueryProp(P_CLASS);
+}
+
+// #####################
+//######################## Material-Handling ############################
+// #####################
+
+// Material setzen
+static mapping _set_material(mapping|string|string* mat )
+{
+ mapping mats = ([]);
+
+ if (mappingp(mat))
+ {
+ if( !sizeof(mat) || !widthof(mat) )
+ raise_error(sprintf("P_MATERIAL: expected mapping with at least one "
+ "key and one value, got %.50O\n",mat));
+ else
+ mats = mat;
+ }
+ else if (stringp(mat))
+ mats[mat]=100;
+ else
+ {
+ int sz = sizeof(mat);
+ // Kommt dann vor, wenn <mat> 0 oder ({}) ist.
+ if ( !sz )
+ raise_error(sprintf("P_MATERIAL: expected string or non-empty "
+ "mapping|string*, got %.50O.\n", mat));
+ mats = mkmapping(mat, allocate(sz, 100/sz));
+ }
+ return Set( P_MATERIAL, mats, F_VALUE );
+}
+
+// Woraus besteht das Objekt?
+static mapping _query_material()
+{
+ mixed res;
+
+ if ( !mappingp(res = Query(P_MATERIAL, F_VALUE)) )
+ return ([MAT_MISC:100]);
+
+ return res;
+}
+
+// Anteil von mat am Objekt?
+int QueryMaterial( string mat )
+{
+ mapping mats;
+
+ if ( !mappingp(mats = QueryProp(P_MATERIAL)) )
+ return 0;
+
+ return mats[mat];
+}
+
+// Anteil der Gruppe am Objekt
+int QueryMaterialGroup( string matgroup )
+{
+ return (int)call_other( MATERIALDB, "MaterialGroup",
+ QueryProp(P_MATERIAL), matgroup );
+}
+
+
+string MaterialList( int casus, mixed idinf )
+{
+ return (string)call_other( MATERIALDB, "ConvMaterialList",
+ QueryProp(P_MATERIAL), casus, idinf );
+}
+
+static int _set_size(int sz) {
+//Groesse muss > 0 sein, alles andere ist unsinnig! (0 und neg. Groessen
+//haben keine phys. Relevanz und machen u.U. Probleme mit Objekten, die
+//Schaden in Abhaengigkeit der Groesse machen)
+ if (sz>0)
+ Set(P_SIZE,sz,F_VALUE);
+ return(Query(P_SIZE,F_VALUE));
+}
+
+// P_CLONE_TIME
+static int _query_clone_time() { return object_time(); }
+