Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/std/container/description.c b/std/container/description.c
new file mode 100644
index 0000000..c4f7b5c
--- /dev/null
+++ b/std/container/description.c
@@ -0,0 +1,103 @@
+// MorgenGrauen MUDlib
+//
+// container/description.c -- standard description for containers
+//
+// $Id: description.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+inherit "/std/thing/description";
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <container.h>
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+
+
+void create()
+{
+  ::create();
+  SetProp(P_TRANSPARENT, 1);
+  AddId("Container");
+}
+
+varargs string long(int mode) {
+  string descr, inv_descr;
+
+  descr = process_string(QueryProp(P_LONG));
+  if(!QueryProp(P_TRANSPARENT)) return descr;
+
+  inv_descr = make_invlist(PL, all_inventory(ME), mode );
+  if ( inv_descr != "" )
+    descr += capitalize(QueryPronoun(WER)) + " enthaelt:\n" + inv_descr;
+  return descr;
+}
+
+// flags: 1 - wizard, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+private void stringenize(mixed obj, int flags, mixed objs, mixed info)
+{
+  string id, tmp;
+  int idx;
+  
+  if ( (!(flags & 4) && (flags & 1))) {
+    //wenn Magier und Magierinfos angehaengt werden sollen:
+    tmp = capitalize(obj->short()||"")[0..<2]
+        + " ["+object_name(obj)+"]";
+  }
+  else if (obj->QueryProp(P_INVIS)) {
+    //keine Ausgabe bzw. leerer String, wird dann von collect rausgeschmissen
+    tmp="";
+  }
+  else {
+    //Spieler, Magier ohne P_WANTS_TO_LEARN etc.
+    tmp = capitalize(obj->short()||"")[0..<2];
+  }
+  //wenn wizard und 'dont collect equal objects': id ist object_name()
+  if(flags & 3 || living(obj)) 
+    id = object_name(obj);
+  //sonst nehmen wir den Namen der Blueprint ->zusammenfassen der Objekte
+  else
+    id = BLUE_NAME(obj) + tmp;
+  // Anzahl des jeweiligen Objekts (anhand von id) zaehlen.
+  if((idx = member(objs, id)) == -1)
+  {
+    objs += ({ id });
+    info += ({ ({ tmp, 1, obj}) });
+  }
+  else
+    info[idx][1]++;
+}
+
+private string collect(mixed obj)
+{
+  //bekommt ein Array ({name,anzahl,objekt})
+  //Objekte ohne Namen (P_SHORT==0 oder P_INVIS) wegwerfen
+  if(!sizeof(obj[0])) return 0;
+  // Objektname + (Anzahl) zurueckgeben.
+  return obj[0] + (obj[1] > 1 ? " ("+obj[1]+")" : "");
+}
+
+// flags: 1 - return array, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+varargs mixed make_invlist(object viewer, mixed inv, int flags)
+{
+  int iswiz;
+  mixed objs, info;
+
+  iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
+  objs = ({}); info = ({});
+  map(inv, #'stringenize/*'*/, iswiz | (flags & 2) | (flags & 4), &objs, &info);
+  if(flags & 1) return info;
+  inv = map(info, #'collect/*'*/) - ({ 0 });
+  if(!sizeof(inv)) return "";
+  return sprintf("%"+(sizeof(inv) > 6 ? "#" : "=")+"-78s",
+                 implode(inv, "\n")) + "\n";
+}
+
diff --git a/std/container/inventory.c b/std/container/inventory.c
new file mode 100644
index 0000000..0a057b7
--- /dev/null
+++ b/std/container/inventory.c
@@ -0,0 +1,128 @@
+// MorgenGrauen MUDlib
+//
+// container/inventory.c -- Umgang mit besonderen Objekten im Inventory
+//
+// $Id: inventory.c 6379 2007-07-20 22:32:02Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <thing.h>
+#include <properties.h>
+#include <sensitive.h>
+
+#define ME this_object()
+
+void RemoveSensitiveObjectFromList(object ob, string list) {
+  mixed a,b,c;
+  int i,f;
+
+  if (!pointerp(a=QueryProp(SENS_PROP_PREFIX+list)))
+    return;
+  f=1;
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (!pointerp(b=a[i]) ||
+	!sizeof(b) ||
+	!objectp(c=b[0]) ||
+	environment(c)!=ME ||
+	c==ob)
+      a[i]=f=0;
+  if (f)
+    return;
+  a-=({0});
+  if (!sizeof(a))
+    a=0;
+  SetProp(SENS_PROP_PREFIX+list,a);
+}
+
+void RemoveSensitiveObject(object ob) {
+  RemoveSensitiveObjectFromList(ob,SENSITIVE_INVENTORY);
+  RemoveSensitiveObjectFromList(ob,SENSITIVE_INVENTORY_TRIGGER);
+}
+
+varargs void InsertSensitiveObjectToList(object ob, string list, string key,
+					 int threshold, mixed *opt) {
+  mixed a;
+  int i;
+  
+  if (!pointerp(a=QueryProp(SENS_PROP_PREFIX+list)))
+    a=({});
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (a[i][SENS_OBJECT]==ob && a[i][SENS_KEY]==key)
+      return;
+  a+=({({ob,symbol_function(SENS_TRIGGER_PREFIX+list,ob),
+         key,threshold,opt})});
+  SetProp(SENS_PROP_PREFIX+list,a);
+}
+
+void InsertSensitiveObject(object ob, mixed arg) {
+  int i;
+  mixed x;
+  mapping map_ldfied;
+  
+  if (!pointerp(arg))
+    return;
+  for (i=sizeof(arg)-1;i>=0;i--)
+    if (pointerp(x=arg[i]) &&
+	sizeof(x)>=3 &&
+	stringp(x[0]) &&
+	stringp(x[1]) &&
+	intp(x[2])) {
+      InsertSensitiveObjectToList(ob,x[0],x[1],x[2],x[3..]);
+      call_other(ME,SENS_INSERT_PREFIX+x[0],ob,x[1],x[2],x[3..]);
+    }
+}
+
+varargs void insert_sensitive_inv(object ob, string key,
+				 int threshold, mixed *opt) {
+  // Diese Funktion sucht, ob beim Hinzufuegen eines sensitiven Objekts
+  // schon ein Objekt da ist, dass dieses ausloest.
+  // z.B. (dynamit, feuer, 100, opt_dynamit) sorgt fuer
+  // dynamit->trigger_sensitive_inv(fackel,feuer,120,opt_fackel,opt_dynamit)
+  // wenn eine Fackel vorher mit Wert 120 eingetragen war.
+  mixed a,x;
+  int i;
+  
+  if (!pointerp(a=QueryProp(P_SENSITIVE_INVENTORY_TRIGGER)) ||
+      !objectp(ob))
+    return;
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (pointerp(x=a[i]) &&
+	x[SENS_KEY]==key &&
+	x[SENS_THRESHOLD]>threshold && // Ist der Ausloeser gross genug?
+	objectp(x[SENS_OBJECT]) &&
+	environment(x[SENS_OBJECT])==environment(ob))
+      ob->trigger_sensitive_inv(x[SENS_OBJECT],x[SENS_KEY],x[SENS_THRESHOLD],
+				x[SENS_OPT],opt);
+  // Zuerst Trigger-Optionen, dann Optionen des sensitiven Objekts
+}
+
+varargs void insert_sensitive_inv_trigger(object ob, string key,
+					 int threshold, mixed *opt) {
+  // Diese Funktion sucht, ob ein sensitives Objekt im Inventory ist,
+  // das durch dieses Objekt ausgeloest wird.
+  // z.B. (fackel, feuer, 120, opt_fackel) sorgt fuer
+  // dynamit->trigger_sensitive_inv(fackel,feuer,120,opt_fackel,opt_dynamit)
+  // wenn Dynamit mit Wert<120 eingetragen war
+  mixed a,x;
+  int i;
+  
+  if (!pointerp(a=QueryProp(P_SENSITIVE_INVENTORY)) ||
+      !objectp(ob))
+    return;
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (pointerp(x=a[i]) &&
+	x[SENS_KEY]==key &&
+	x[SENS_THRESHOLD]<threshold && // Ausloeser gross genug?
+	objectp(x[SENS_OBJECT]) &&
+	environment(x[SENS_OBJECT])==environment(ob) &&
+	closurep(x[SENS_CLOSURE]))
+      funcall(x[SENS_CLOSURE],
+	      ob,key,threshold,opt,x[SENS_OPT]);
+  // Zuerst Trigger-Optionen, dann Optionen des sensitiven Objekts
+}
diff --git a/std/container/items.c b/std/container/items.c
new file mode 100644
index 0000000..9961bfe
--- /dev/null
+++ b/std/container/items.c
@@ -0,0 +1,256 @@
+// MorgenGrauen MUDlib
+//
+// container/items.c -- creating extra items in room
+//
+// $Id: items.c 8811 2014-05-09 17:30:37Z Zesstra $
+
+// extra items handling
+//
+//	AddItem(string filename, int refresh)
+//	  Clones an item and puts it into the room. <refresh> may be
+//	  on of the following:
+//	  REFRESH_NONE: No refresh done until reboot.
+//	  REFRESH_DESTRUCT: Refresh on reset if item was destructed.
+//	  REFRESH_REMOVE: Refresh on reset if item was removed from room.
+//	  REFRESH_ALWAYS: Create a new clone on every reset.
+//
+// The commands are implemented as properties P_ITEMS as mapping. They are 
+// stored locally (_set_xx) as mapping to speed up the routines 
+// in this module.
+// 
+// Format of key and data in the P_ITEMS mapping:
+//
+// ([ key1 : refresh1; obp1; arr1, ..., keyn : refreshn; obpn; arrn ])
+
+#include <sys_debug.h>
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <rooms.h>
+#include <container.h>
+#undef NEED_PROTOTYPES
+#include <defines.h>
+#include <config.h>
+#include <properties.h>
+#include <moving.h>
+#include <daemon.h>
+
+protected void create()
+{ 
+    Set( P_ITEMS, ({}) );
+    Set( P_ITEMS, SECURED, F_MODE_AS );
+    
+    OBJECTD->QueryObject();  // querying general objects
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+/* Kram zum Aufraeumen von multiplen gleichen Items im Container. */
+private object removeable_ob( object ob )
+{
+    if ( !query_once_interactive(ob) && !living(ob) )
+        return ob;
+
+    return 0;
+}
+
+protected varargs void remove_multiple(int limit, mixed fun)
+{
+    object *inh = all_inventory(this_object()) - ({0});
+
+    inh = filter( inh, #'removeable_ob );
+    foreach(mixed item : QueryProp(P_ITEMS))
+      inh -= ({ item[0] });
+ 
+    if (!stringp(fun) && !closurep(fun))
+      fun = "description_id";
+    inh = unique_array(inh, fun, 0);
+    foreach(mixed arr: inh)
+    {
+      if (sizeof(arr) <= limit)
+        continue;
+      catch(call_other(arr[limit ..], "remove"); publish);
+    }
+}
+
+
+/* Item handling */
+public varargs object AddItem( mixed filename, int refresh, mixed props ) 
+{
+    string file;
+    object ob;
+    int i;
+
+    if( pointerp(filename) ) {
+        for( i = sizeof(filename); i--; )
+            filename[i] = (string)master()->_get_path( filename[i], "?" );
+        
+        file = filename[random( sizeof(filename) )];
+    }
+    else 
+        file = filename = (string)master()->_get_path( filename, "?" );
+    
+    if ( props == 1 )
+        catch(ob = load_object( file); publish);
+    else
+	catch(ob = clone_object(file); publish);
+    
+    if (objectp(ob)) {
+      ob->move( this_object(), M_NOCHECK|M_NO_ATTACK );
+      // mit Absicht keine Pruefung aufs Move, wenns nicht geht, solls 2s
+      // spaeter auf der Ebene buggen, weil praktisch niemand im create() das
+      // Ergebnis vom AddItem() prueft.
+    }
+
+    // In P_ITEMS vermerken, es sei denn, REFRESH_NONE ist gegeben, in dem
+    // Fall ist die Speicherung voellig unnoetig.
+    // TODO: Pruefen, ob das wirklich problemlos geht. Bis dahin werden auch
+    // TODO::REFRESH_NONE-Items vermerkt. (s. clean_up() in /std/room.c)
+    //if (!(refresh & REFRESH_NONE)) {
+      SetProp( P_ITEMS, QueryProp(P_ITEMS) +
+             ({ ({ ob,        // RITEM_OBJECT
+                   filename,  // RITEM_FILE
+                   refresh    // RITEM_REFRESH
+                       }) +
+                    ((mappingp(props) || props == 1) ? ({ props }) : ({})) }) );
+    //}
+
+    if ( ob && mappingp(props) ) 
+        walk_mapping( props, symbol_function( "SetProp", ob ) );
+    
+    return ob;
+}
+
+
+private void ri_rem_ob( object ob )
+{
+    object *inv;
+    int i;
+  
+    if ( objectp(ob) && present(ob, this_object()) ) {
+        inv = deep_inventory(ob);
+        
+        for ( i = sizeof(inv); i--; )
+            if ( inv[i] ) {
+                inv[i]->remove(1);
+                
+                if ( inv[i] )
+                    destruct(inv[i]);
+            }
+        
+        ob->remove(1);
+        
+        if ( ob )
+            destruct(ob);
+    }
+}
+
+
+private int ri_filter( mixed *ritem, mixed file )
+{
+    object ob, *inv;
+    int i;
+  
+    ob = ritem[RITEM_OBJECT];
+  
+    if ( stringp(file) && ritem[RITEM_FILE] == file )
+        return ri_rem_ob(ob), 0;
+    else if ( pointerp(ritem[RITEM_FILE]) && pointerp(file) &&
+              sizeof(file & ritem[RITEM_FILE]) == sizeof(ritem[RITEM_FILE]) )
+        return ri_rem_ob(ob), 0;
+
+    return 1;
+}
+
+
+public void RemoveItem( mixed filename )
+{
+    mixed *items;
+    int i;
+
+    if ( !pointerp(items = QueryProp(P_ITEMS)) || !sizeof(items) )
+        return;
+
+    if ( pointerp(filename) )
+        for ( i = sizeof(filename); i--; )
+            filename[i] = (string)master()->_get_path( filename[i], "?" );
+    else
+        filename = (string)master()->_get_path( filename, "?" );
+  
+    SetProp( P_ITEMS, filter( items, #'ri_filter/*'*/, filename ) );
+}
+
+
+private mixed _do_refresh( mixed item )
+{
+    string file;
+    object ob;
+
+    if ( !pointerp(item) || item[RITEM_REFRESH] == REFRESH_NONE )
+        return item;
+
+    if ( pointerp(item[RITEM_FILE]) ) 
+        file = item[RITEM_FILE][random( sizeof(item[RITEM_FILE]) )];
+    else
+        file = item[RITEM_FILE];
+
+    switch( item[RITEM_REFRESH] ){
+    case REFRESH_MOVE_HOME:
+        if ( objectp(item[RITEM_OBJECT]) &&
+             environment(item[RITEM_OBJECT]) != ME ) {
+            item[RITEM_OBJECT]->move( ME, M_GO|M_NO_ATTACK );
+            break;
+        }
+        
+    // fall through
+    case REFRESH_DESTRUCT:
+        if ( objectp(item[RITEM_OBJECT]) ) 
+            break; // else FALL THROUGH
+        
+    case REFRESH_REMOVE:
+        if ( objectp(item[RITEM_OBJECT]) &&
+             environment(item[RITEM_OBJECT]) == ME )
+            break; // else FALL THROUGH
+        
+    default:
+        if ( sizeof(item) > RITEM_PROPS && item[RITEM_PROPS] == 1 ) {
+            ob = load_object(file);
+        }
+        else
+            ob = clone_object(file);
+
+        ob->move( ME, M_NOCHECK|M_NO_ATTACK );
+        break;
+    }
+    
+    if ( ob ){
+        item[RITEM_OBJECT] = ob;
+        
+        if ( sizeof(item) > RITEM_PROPS && mappingp(item[RITEM_PROPS]) )
+            walk_mapping( item[RITEM_PROPS], symbol_function( "SetProp", ob ) );
+    }
+    
+    return item;
+}
+
+
+// reset handling: check how the items should be refreshed.
+void reset() 
+{
+    mixed *items;
+
+    if ( !pointerp(items = QueryProp(P_ITEMS)) ){
+        SetProp( P_ITEMS, ({}) );
+        return;
+    }
+    
+    SetProp( P_ITEMS, map( items, #'_do_refresh/*'*/ ) - ({0}) );
+}
+
diff --git a/std/container/light.c b/std/container/light.c
new file mode 100644
index 0000000..435400f
--- /dev/null
+++ b/std/container/light.c
@@ -0,0 +1,149 @@
+// MorgenGrauen MUDlib
+//
+// container/light.c -- Lichtsystem fuer Container
+//
+// $Id: description.c 6986 2008-08-22 21:32:15Z Zesstra $
+
+inherit "/std/thing/light";
+
+#pragma strict_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <container.h>
+#include <properties.h>
+
+// BTW: _query_last_content_change() sollte bloss nie auch -1 fuer
+// uninitialisiert zurueckliefern...
+private nosave int last_light_calc = -1;
+
+protected void create()
+{
+  ::create();
+  // wieviel Licht schluckt der Container _muss_ > 0 sein!
+  SetProp(P_LIGHT_TRANSPARENCY, 2);
+}
+
+protected void create_super() {set_next_reset(-1);}
+
+
+static mixed _query_light_transparency()
+{
+   if (QueryProp(P_TRANSPARENT))
+     return Query(P_LIGHT_TRANSPARENCY,F_VALUE);
+   return 999;
+}
+
+public int add_light_sources(int * sources) {
+  float light = 0.0;
+  //printf("als(%O): %O\n",this_object(),sources);
+  // Alle Lichtlevel werden als Exponent von e aufgefasst und die Summe dieser
+  // Potenzen gebildet.
+  foreach(int l : sources) {
+     if (l > 0)
+       light += exp(l);
+     else if (l < 0)
+       light -= exp(abs(l));
+     // l==0 muss ignoriert werden
+  }
+  // anschliessend wird aus dieser Summe der natuerliche Logarithmus
+  // berechnet.
+  // auf diese Weise haben hoehere Lichtlevel ueberproportional viel Einfluss
+  // auf die Helligkeit im Raum, kleine Lichtlevel fallen aber nicht komplett
+  // raus.
+  if (light > 0) {
+      light = log(light);
+  }
+  else if (light < 0) {
+      light = -log(abs(light));
+  }
+
+  //printf("als(): %O\n",light);
+  // runden und als int zurueckgeben.
+  if (light >= 0)
+    return to_int(light+0.5);
+  //else
+  return to_int(light-0.5);
+}
+
+static int _query_total_light()
+{
+  int totallight;
+
+  if ( _query_last_content_change() == last_light_calc )
+    return Query(P_TOTAL_LIGHT);
+
+  // eigenes P_LIGHT und P_TOTAL_LIGHT der enthaltenen Objekte verrechnen
+  int intlight = add_light_sources(
+      all_inventory()->QueryProp(P_TOTAL_LIGHT) + ({QueryProp(P_LIGHT)}));
+  // P_INT_LIGHT (gerundet) abspeichern
+  Set(P_INT_LIGHT, intlight, F_VALUE);
+
+  // Licht nach aussen muss Containertransparenz beruecksichtigen.
+  if (intlight > 0) {
+    totallight = intlight - QueryProp(P_LIGHT_TRANSPARENCY);
+    if (totallight < 0)
+      totallight = 0;
+  }
+  else {
+    totallight = intlight + QueryProp(P_LIGHT_TRANSPARENCY);
+    if (totallight > 0)
+      totallight = 0;
+  }
+  //printf("_query_total_light(%O): %O\n",this_object(),totallight);
+
+  last_light_calc = _query_last_content_change();
+
+  // Runden und Konversion nach int nicht vergessen...
+  if (totallight > 0)
+    return SetProp(P_TOTAL_LIGHT, to_int(totallight + 0.5));
+  //else
+  return SetProp(P_TOTAL_LIGHT, to_int(totallight - 0.5));
+}
+
+static int _query_int_light()
+{
+   int intlight, envlight;
+
+   if ( _query_last_content_change() != last_light_calc )
+       _query_total_light();
+
+   // P_INT_LIGHT des environments kann sich natuerlich aendern _ohne_ das
+   // etwas an einem container geaendert wird. Daher Auswertung jedes mal
+   // neu aktualisieren.
+   if (!environment()
+       || !(envlight=(int)environment()->QueryProp(P_INT_LIGHT)))
+      return Query(P_INT_LIGHT, F_VALUE);
+   else {
+      intlight = Query(P_INT_LIGHT, F_VALUE);
+      int transparency = QueryProp(P_LIGHT_TRANSPARENCY);
+
+      // bei einem transparentem Container kann natuerlich das Licht des
+      // environments den Container auch ausleuchten....
+      // (Anmerkung: in dem Licht von draussen ist unser Lichtanteil
+      // natuerlich auch drin, gedaempft um die Transparenz (raus und rein).
+      // Ich vernachlaessige das.)
+      if (abs(envlight) > transparency) {
+        if (envlight > 0)
+            envlight -= transparency;
+        else
+            envlight += transparency;
+        // jetzt Licht von aussen und innen verrechnen
+        intlight = add_light_sources(({intlight, envlight}));
+      }
+      // else: nur P_INT_LIGHT, Licht des Environments keine Bedeutung
+   }
+   return intlight;
+}
+
+static int _set_light(int light)
+{
+  last_light_calc=-1;
+
+  return ::_set_light(light);
+}
+
diff --git a/std/container/moneyhandler.c b/std/container/moneyhandler.c
new file mode 100644
index 0000000..ae25453
--- /dev/null
+++ b/std/container/moneyhandler.c
@@ -0,0 +1,53 @@
+// MorgenGrauen MUDlib
+//
+// container/moneyhandler.c -- money handler for container
+//
+// $Id: moneyhandler.c 6738 2008-02-19 18:46:14Z Humni $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <moving.h>
+#include <money.h>
+
+public int AddMoney( int amount )
+{
+  object ob;
+  int ret;
+
+  if ( !amount )
+    return 1;
+    
+  ob = clone_object( GELD );
+  ob->SetProp( P_AMOUNT, amount );
+
+  ret=ob->move( this_object(), M_PUT|M_MOVE_ALL );
+  // Bei fehlerhaftem move ggf. wieder zerstoeren.
+  if ((ret != MOVE_OK)
+      && ob)
+  {
+    ob->remove(1);
+  }
+  return ret;
+}
+
+public int QueryMoney()
+{
+  object money;
+  int geld;
+
+  if ( money = present_clone(GELD, this_object()) )
+    geld = money->QueryProp(P_AMOUNT);
+
+  if ( money = present(GELDBOERSE_MIT_GELD, this_object()) )
+    geld += money->QueryProp(P_AMOUNT);
+
+  return geld;
+}
diff --git a/std/container/restrictions.c b/std/container/restrictions.c
new file mode 100644
index 0000000..3da5c9d
--- /dev/null
+++ b/std/container/restrictions.c
@@ -0,0 +1,480 @@
+// MorgenGrauen MUDlib
+//
+// container/restrictions.c -- container restrictions
+//
+// $Id: restrictions.c 9020 2015-01-10 21:49:41Z Zesstra $
+
+// This is a simple container to put objects in. It defines all functions
+// which are necessary to describe an object which can be filled with
+// other things.
+//
+// It will support restrictions for volume, weight etc.
+//
+// The following properties are defined:
+// P_MAX_WEIGHT - maximum weight which container can carry
+// P_TOTAL_WEIGHT - total weight, with contents.
+// P_WEIGHT - the weight of the container without contents
+//
+// Functions for manipulation of weight
+// MayAddWeight(weight) - Can <weight> be inserted?
+// AddWeight(weight) - Add an amount of <weight>
+//
+// IMPORTANT: unit equals 1 gram
+
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include "/sys/thing/properties.h"
+#include <properties.h>
+#include <defines.h>
+#include <thing/description.h>
+
+// local properties prototypes
+static int _query_total_weight();
+
+
+private nosave int LastWeightCalc, contents, LastObjectCount, objcount;
+private nosave int last_content_change;
+
+
+void create()
+{
+    Set( P_WEIGHT_PERCENT, 50 );
+    Set( P_TOTAL_WEIGHT, NOSETMETHOD, F_SET_METHOD );
+    Set( P_TOTAL_WEIGHT, PROTECTED, F_MODE );
+    Set( P_MAX_OBJECTS, 100 );
+    Set( P_TOTAL_OBJECTS, NOSETMETHOD, F_SET_METHOD );
+    Set( P_TOTAL_OBJECTS, PROTECTED, F_MODE );
+    LastWeightCalc = -1;
+    LastObjectCount = -1;
+}
+
+
+static int _query_last_content_change()
+{
+    return last_content_change;
+}
+
+
+// absichtlich public, da es von der simul_efun.c *direkt* aufgerufen
+// wird aus Performancegruenden!
+public varargs int _set_last_content_change( mixed wert )
+{
+    // Der uebergebene Wert ist unerheblich. Die Property
+    // P_LAST_CONTENT_CHANGE wird so oder so bei jedem SetProp()-
+    // Aufruf um eins erhoeht, um der 2s-"Unschaerfe" von time()
+    // aus dem Weg zu gehen.
+    return ++last_content_change;
+}
+
+
+int query_weight_contents()
+{
+    object *objs;
+    int w, w2, i;
+
+    if ( last_content_change == LastWeightCalc )
+        return contents;
+    
+    w = 0;
+    objs = all_inventory(this_object());
+    i = sizeof(objs);
+    
+    while ( i-- ){
+        if ( !(w2 = (int)objs[i]->QueryProp(P_TOTAL_WEIGHT)) )
+            w2 = (int)objs[i]->QueryProp(P_WEIGHT);
+        w += w2;
+    }
+    
+    LastWeightCalc = last_content_change;
+    return contents = w;
+}
+
+
+static int _query_total_objects()
+{
+  if ( last_content_change == LastObjectCount )
+      return objcount;
+
+  objcount = sizeof( filter_objects( all_inventory(), "short" ) );
+  LastObjectCount = last_content_change;
+  return objcount;
+}
+
+
+// diese Funktion sollte von Raeumen natuerlich ueberschrieben werden...
+int MayAddObject( object ob )
+{
+    if (ob) {
+        if ( !ob->short() )
+            return 1; // invis-Objekte duerfen immer
+        
+        if ( ob == ME || environment(ob) == ME)
+            return 1; // objekt ist schon drin
+    }
+    
+    return (QueryProp(P_TOTAL_OBJECTS) < QueryProp(P_MAX_OBJECTS));
+}
+
+
+#define ENV environment
+#define PO previous_object()
+
+int MayAddWeight( int w )
+{
+    int nw, aw;
+
+    // was nix wiegt, passt
+    if ( w <= 0 )
+        return 0;
+
+    nw = w; // Gewicht im neuen Container
+    aw = 0; // Gewicht fuer das env()
+
+    // Von Raum in Behaelter im Spieler: Container sieht volles Gewicht (nw=w),
+    // Check im env() (Spieler) mit reduziertem Gewicht.
+    if ( ENV() && PO && ENV(PO) != ENV() )
+        aw = QueryProp(P_WEIGHT_PERCENT) * w / 100 || 1;
+
+    if ( PO && ENV(PO) && ENV(ENV(PO)) ){
+        // Wenn das Objekt ein env() hochbewegt wird, wird das Gewicht im alten
+        // Container abgezogen. Weiterer Check im env() ist unnoetig (aw=0).
+        if ( ENV(ENV(PO)) == ME )
+            nw = w - (int)ENV(PO)->QueryProp(P_WEIGHT_PERCENT) * w / 100;
+        // Eine Ebene tiefer bewegen: Test im neuen Container mit unveraendertem
+        // Gewicht; Check im env() mit um Gewichtsreduktion verringertem Gewicht
+        else if ( present( ME, ENV(PO) ) )
+            aw = QueryProp(P_WEIGHT_PERCENT) * w / 100 - w;
+        // Auf derselben Ebene verschieben (von Paket in Beutel):
+        // Neuer Container sieht volles Gewicht (nw=w), Check im env() mit
+        // Differenz aus Gewicht in Container1 und in Container2.
+        else if ( ENV(ENV(ENV(PO))) && ENV() == ENV(ENV(PO)) )
+            aw = QueryProp(P_WEIGHT_PERCENT) * w / 100
+                - ((int)ENV(PO)->QueryProp(P_WEIGHT_PERCENT) * w / 100 || 1);
+    }
+
+    if ( query_weight_contents() + nw > QueryProp(P_MAX_WEIGHT) )
+        // Container kann Gewicht nicht mehr aufnehmen
+        return -1;
+    
+    if ( aw && ENV()->MayAddWeight(aw) < 0 )
+        // Umgebung des Containers kann Gewicht nicht mehr aufnehmen
+        return -2;
+    
+    return 0;
+}
+
+
+/* Redefine PreventInsert() to prevent inserting of special objects. If
+ * a value greater 0 is returned the object ob can't be inserted in the
+ * container.
+ */
+public int PreventInsert( object ob ) { return 0; }
+public int PreventLeave( object ob, mixed dest ) { return 0; }
+public int PreventInsertLiving( object ob ) { return 0; }
+public int PreventLeaveLiving( object ob, mixed dest ) { return 0; }
+public void NotifyInsert(object ob, object oldenv) { }
+
+// **** local property methods
+static int _query_total_weight() 
+{ 
+    return QueryProp(P_WEIGHT) +
+        (QueryProp(P_WEIGHT_PERCENT) * query_weight_contents() / 100); 
+}
+
+
+// Hilfsfunktion
+static int _behalten( object ob, string uid )
+{
+    return (string)ob->QueryProp(P_KEEP_ON_SELL) == uid;
+}
+
+
+/*
+ *
+ * get a list of all contained objects matching a complex description
+ *
+ */
+
+#define POS_INVERS   0x01  /* nur zur funktionsinternen Verwendung */
+#define POS_LETZTES  0x02  /* nur zur funktionsinternen Verwendung */
+
+object *present_objects( string complex_desc )
+{
+    int i;          // Zaehlervariable
+    int meth;       // 0x01 = invers?,  0x02 = letztes?
+    object ob;      // einzelnes temporaeres Objekt
+    object *obs;    // liste der ausgewaehlten Objekte
+    object *erg;    // zum sammeln bis es nachher zurueckgegeben wird...
+    string *strlst; // liste aller Gruppen die mit AND verknuepft sind
+    object haufen;
+
+    strlst = allocate(2);
+    
+    if ( sscanf( complex_desc, "%s ausser %s", strlst[0], strlst[1]) == 2 ){
+        erg = present_objects( strlst[0] );
+        obs = present_objects( strlst[1] );
+        return erg-obs;
+    }
+    
+   strlst = explode( complex_desc, " und " );
+   erg = ({});
+   
+   for ( i = sizeof(strlst); i--; )
+   {
+       complex_desc = strlst[i];
+       // auf letzte/letztes/letzten pruefen...
+       if ( complex_desc[0..5] == "letzte" ){
+           switch( complex_desc[6..6] ){
+           case " ":
+               meth |= POS_LETZTES;
+               complex_desc = complex_desc[7..];
+               break;
+           case "s":
+           case "n":
+           case "m":
+           case "r":
+               if ( complex_desc[7..7] != " " )
+                   break;
+               meth |= POS_LETZTES;
+               complex_desc = complex_desc[8..];
+               break;
+           default:
+           }
+       }
+
+       // auf verneinung pruefen
+       if ( complex_desc[0..5] == "nicht " ){
+           meth |= POS_INVERS;
+           complex_desc = complex_desc[6..];
+       }
+
+       obs=({});
+       // nun nach Main-Ids (Gruppen) suchen...
+       if ( meth & POS_LETZTES )
+       { // geht es nur um den letzten Gegenstand?
+           switch( complex_desc ){
+           case "waffe":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_WEAPON_TYPE );
+               break;
+               
+           case "ruestung":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_ARMOUR_TYPE );
+               break;
+          
+           case "kleidung":
+               obs = filter_objects( all_inventory(), "IsClothing");
+               break;
+               
+           case "verschiedenem":
+           case "verschiedenes":
+               obs = all_inventory();
+               obs -= filter_objects( obs, "QueryProp", P_WEAPON_TYPE );
+               obs -= filter_objects( obs, "QueryProp", P_ARMOUR_TYPE );
+               obs -= filter_objects( obs, "IsClothing");
+               obs -= filter( obs, #'living/*'*/ );
+               break;
+        
+           case "eigenes":
+           case "meins":
+               if (objectp(haufen=present("\nhaufen "+
+                     this_player()->name(WEM)))) {
+                  obs = all_inventory(haufen);
+               }
+               // kein break;, Fall-through!
+           case "behaltenem":
+           case "behaltenes":
+               obs += filter( all_inventory(), "_behalten", ME,
+                                   getuid(this_player() || previous_object()) );
+
+               obs += (QueryProp(P_ARMOURS) || ({}))
+                   + ({ QueryProp(P_WEAPON) }) - ({ 0 });
+                   break;
+                   
+           case "gegenstand":
+               obs = all_inventory() -
+                   filter( all_inventory(), #'living/*'*/ );
+               break;
+               
+           default:
+               obs = filter_objects( all_inventory(), "id", complex_desc );
+           }
+
+           // unsichtbare objekte entfernen
+           obs = filter_objects( obs, "short" );
+
+           // letzten Gegenstand raussuchen
+           obs = obs[0..0]; 
+       } // if (letzter Gegenstand)
+       else
+       { // ganze Gruppen und nicht nur das letzte Objekt
+           switch ( complex_desc )
+           {
+           case "allem":
+           case "alles":
+           case "jeden gegenstand":
+           case "jedem gegenstand":
+           case "gegenstaende":
+           case "alle gegenstaende":
+           case "allen gegenstaenden":
+               if ( meth & POS_INVERS )
+                   continue; // alles nicht = nichts :)
+               
+               obs = all_inventory() -
+                   filter( all_inventory(), #'living/*'*/ );
+               break;
+               
+           case "waffen":
+           case "jede waffe":
+           case "jeder waffe":
+           case "alle waffen":
+           case "allen waffen":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_WEAPON_TYPE );
+               break;
+               
+           case "ruestungen":
+           case "jede ruestung":
+           case "jeder ruestung":
+           case "alle ruestungen":
+           case "allen ruestungen":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_ARMOUR_TYPE );
+               break;
+ 
+           case "kleidung":
+           case "jede kleidung":
+           case "jeder kleidung":
+           case "alle kleidung":
+           case "allen kleidung":
+               obs = filter_objects( all_inventory(), "IsClothing");
+               break;
+              
+           case "gegenstand":
+               obs = filter_objects( all_inventory() -
+                                     filter( all_inventory(),
+                                                   #'living/*'*/ ),
+                                     "short" )[0..0];
+               break;
+               
+           case "verschiedenem":
+           case "verschiedenes":
+               obs = all_inventory();
+               obs -= filter_objects( obs, "QueryProp", P_WEAPON_TYPE );
+               obs -= filter_objects( obs, "QueryProp", P_ARMOUR_TYPE );
+               obs -= filter_objects( obs, "IsClothing");
+               obs -= filter( obs, #'living/*'*/ );
+               break;
+       
+           case "eigenes":
+           case "eigenem":
+           case "eigenen":
+           case "meins":
+           case "alles eigene":
+               if (objectp(haufen=present("\nhaufen "+
+                       this_player()->name(WEM)))) {
+                  obs = all_inventory(haufen);
+               }
+               // kein break;, Fall-through!
+           case "behaltenem":
+           case "behaltenen":
+           case "behaltenes":
+           case "alles behaltene":
+               obs += filter( all_inventory(), "_behalten", ME,
+                                   getuid(this_player() || previous_object()) );
+
+               obs += (QueryProp(P_ARMOURS) || ({}))
+                   + ({ QueryProp(P_WEAPON) }) - ({ 0 });
+                   break;
+                   
+           default:
+               if ( complex_desc[0..3] == "jede" ||
+                    complex_desc[0..4] == "alle ")
+               {
+                   if ( complex_desc[4..4] == " " )
+                   {
+                       obs = filter_objects( all_inventory(), "id",
+                                             complex_desc[5..] );
+                       break;
+                   }
+                   else
+                   {
+                       switch( complex_desc[4..5] )
+                       {
+                       case "m ":
+                       case "r ":
+                       case "n ":
+                       case "s ":
+                           obs = filter_objects( all_inventory(), "id",
+                                                 complex_desc[6..] );
+                           break;
+                           
+                       default:
+                           obs = 0;
+                       }
+                       if (obs)
+                           break;
+                   }
+               }
+
+               // Der Normalfall: einzelne ID...
+               ob = present( complex_desc, ME );
+               // Achtung: dieser Teil setzt das for() fort (continue) und
+               // umgeht dabei die Pruefung auf Sichtbarkeit nach dem Ende vom
+               // switch(). Aus diesem Grunde muss hier selber geprueft
+               // werden.
+               if ( meth & POS_INVERS )
+               {
+                   if ( ob && ob != ME )
+                       erg += (filter_objects( all_inventory(), "short" )
+                               - ({ ob }) );
+                   else
+                       erg += filter_objects( all_inventory(), "short" );
+               }
+               else if ( ob && ob != ME && !ob->QueryProp(P_INVIS) )
+                   erg += ({ ob });   //Normalfall: einzelne ID
+
+               continue;
+
+           }  // switch
+           // unsichtbare objekte entfernen
+           obs = filter_objects( obs, "short" ); 
+       } // else
+
+       if ( meth & POS_INVERS )
+           erg += ( filter_objects( all_inventory(), "short" ) - obs );
+       else
+           erg += obs;
+   } // for
+   return erg;
+}
+
+/*
+ * returns a list of all found objects inside itself
+ * may call same function in objects inside
+ *
+ * Funktion wird nicht mehr von put_and_get aufgerufen, stattdessen wird
+ * direkt present_objects benutzt!
+ */ 
+object *locate_objects( string complex_desc, int info ) {
+    string was, wo;
+
+    if ( sscanf( complex_desc, "%s in %s", was, wo ) == 2 ){
+      object *found_obs = ({});
+      foreach(object invob: present_objects(wo)) {
+        // || ({}) weil invob ein Objekt ohne locate_objects() sein koennte.
+        found_obs += (object *)invob->locate_objects( was, info) || ({});
+      }
+      return found_obs;
+    }
+    // kein "in" gefunden
+    return present_objects( complex_desc );
+}
+