Verwaltung fuer virtuell anwesende Items.

Eine Verwaltung fuer vItems, welche aehnlich zu
AddItem() funktioniert.
Entscheidend ist hier, dass die vItems nicht echt im
Container anwesend sind, sondern nur virtuell (sie haben
kein Environment).
Davon unabhaengig, werden sie aber von viewcmd.c und
put_and_get.c beruecksichtigt.

Change-Id: If7147c04571ec2d88cfbea8ddeb7c45ddeeecbd9
diff --git a/std/armour_container.c b/std/armour_container.c
index 604741f..bc45280 100644
--- a/std/armour_container.c
+++ b/std/armour_container.c
@@ -18,6 +18,7 @@
 inherit "/std/container/restrictions";
 inherit "/std/container/inventory";
 inherit "/std/container/items";
+inherit "/std/container/vitems";
 inherit "/std/clothing/moving";
 inherit "/std/armour/wear";
 inherit "/std/armour/combat";
@@ -50,6 +51,7 @@
 
 void reset()  {
   items::reset();
+  vitems::reset();
   envchk::reset();
 }
 
diff --git a/std/clothing_container.c b/std/clothing_container.c
index 5ee5a94..995e675 100644
--- a/std/clothing_container.c
+++ b/std/clothing_container.c
@@ -18,6 +18,7 @@
 inherit "/std/container/restrictions";
 inherit "/std/container/inventory";
 inherit "/std/container/items";
+inherit "/std/container/vitems";
 inherit "/std/clothing/moving";
 inherit "/std/clothing/wear";
 inherit "/std/clothing/container_description";
@@ -48,6 +49,7 @@
 
 void reset()  {
   items::reset();
+  vitems::reset();
   envchk::reset();
 }
 
diff --git a/std/container.c b/std/container.c
index d3ff7df..a39beee 100644
--- a/std/container.c
+++ b/std/container.c
@@ -27,6 +27,7 @@
 inherit "/std/container/restrictions";
 inherit "/std/container/inventory";
 inherit "/std/container/items";
+inherit "/std/container/vitems";
 inherit "/std/thing/envchk";
 
 #include <properties.h>
@@ -55,6 +56,7 @@
 void reset()
 {
   items::reset();
+  vitems::reset();
   envchk::reset();
 }
 
diff --git a/std/container/vitems.c b/std/container/vitems.c
new file mode 100644
index 0000000..acd08b2
--- /dev/null
+++ b/std/container/vitems.c
@@ -0,0 +1,310 @@
+// MorgenGrauen MUDlib
+//
+// container/vitems.c -- managing virtually present automatic items
+//
+#pragma strict_types,rtt_checks, range_check, pedantic
+#pragma no_clone
+
+#include <properties.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <container/vitems.h>
+
+// Werte im Mapping in P_VITEMS:
+#define VI_PATH          0    // Pfad zur BP
+#define VI_REFRESH       1    // Refresheinstellung, s. AddItem
+#define VI_OBJECT        2    // Aktuelles Objekt des VItem
+#define VI_PROPS         3    // Props fuer das echte Objekt
+#define VI_SHADOW_PROPS  4    // Props fuer den shadow/vitem_proxy
+#define VI_LAST_OBJ      5    // letztes mitgenommenes Objekt
+#define VI_LENGTH        6
+
+// VI_REFRESH hat aehnliche Bedeutung wie bei AddItem, mit einer Besonderheit:
+// intern wird in Form eines negativen Wertes kodiert, ob das VItem gerade neu
+// erzeugt werden darf (negativ: ja, positiv: nein).
+
+/* Mehr zu VI_REFRESH:
+ * vitems raeumen sich ggf. selber weg. D.h. wenn der Clone des vitems nicht
+ * mehr ist, heisst das nicht, dass es jemand mitgenommen hat. In dem Fall
+ * muss es sofort (wenn seine Praesenz abgefragt wird) neu erzeugt werden.
+ * Die Logik ist wie folgt:
+ * Der Standardzustand von VI_REFRESH ist ein negierter Wert. Solange dies der
+ * Fall ist, wird ein vitem *immer* erzeugt, sofern es abgefragt wird, aber
+ * nicht da ist.
+ * Wird ein vitem hingegen bewegt/mitgenommen, wird VI_REFRESH auf den
+ * positiven Wert geaendert. In diesem Fall wird es NICHT mehr neu erzeugt,
+ * wenn es nicht als vitem praesent ist. Erst im naechsten reset() wird
+ * geprueft, ob es in Zukunft wieder neu erzeugt werden darf. Wenn ja, wird
+ * der Zahlenwert wieder negiert.
+ * Es ist Absicht, dass VI_REFRESH_ALWAYS auch nur maximal einmal pro reset()
+ * *mitgenommen* werden darf.
+ */
+
+
+//protected void create()
+//{
+//}
+
+public varargs void RemoveVItem(string key)
+{
+  mixed vitems=QueryProp(P_VITEMS);
+  if (mappingp(vitems) && member(vitems, key))
+  {
+    // Es wird auch zerstoert, wenn das genommene Objekt gerade im Raum
+    // rumliegt (weil Spieler es hat fallen lassen etc.)
+    if (vitems[key, VI_OBJECT])
+        vitems[key, VI_OBJECT]->remove(1);
+    if (vitems[key, VI_LAST_OBJ]
+        && environment(vitems[key, VI_LAST_OBJ]) == this_object())
+        vitems[key, VI_LAST_OBJ]->remove(1);
+
+    m_delete(vitems, key);
+    SetProp(P_VITEMS, vitems);
+  }
+}
+
+// TODO: braucht es dynamische refresh, shadowprops und props?
+public varargs void AddVItem(string key, int refresh, mapping shadowprops,
+                             string path, mapping props)
+{
+  if (!sizeof(key))
+    return;
+  // Wenn path gegeben, muss es eine ladbare Blueprint sind
+  if (sizeof(path) && !load_object(path))
+    return;
+  if (!sizeof(path))
+  {
+    if (mappingp(props))
+      raise_error("Reine vitems erlauben keine <props>\n");
+  }
+
+  refresh ||= VI_REFRESH_NONE;
+  shadowprops ||= ([]);
+  // Wenn reines vItem und keine IDs gesetzt, wird <key> als ID verwendet,
+  // irgendwie muss es ja ansprechbar sein. (Wenn es ein Objekt mit Templat
+  // ist, hat es normalerweise die IDs aus dem Templat. Wenn man das nicht
+  // will, muss man es mit gezielter Angabe von P_IDS in den Shadowprops
+  // ueberschreiben.) Gleiches fuer P_NAME (ohne ist ein "Ding") und P_SHORT
+  // (ohne P_SHORT ist es unsichtbar)
+  if (!path)
+  {
+    if (!member(shadowprops, P_IDS))
+      shadowprops[P_IDS] = ({key});
+    if (!member(shadowprops, P_NAME))
+      shadowprops[P_NAME] = capitalize(key);
+    if (!member(shadowprops, P_SHORT))
+      shadowprops[P_SHORT] = capitalize(key);
+  }
+  mixed vitems=QueryProp(P_VITEMS);
+  if (!mappingp(vitems))
+    vitems = m_allocate(1, VI_LENGTH);
+  vitems[key, VI_PATH] = path;
+  vitems[key, VI_REFRESH] = negate(refresh);
+  vitems[key, VI_PROPS] = props;
+  vitems[key, VI_SHADOW_PROPS] = shadowprops;
+  SetProp(P_VITEMS, vitems);
+}
+
+
+private void configure_object(object ob, mapping props)
+{
+  foreach (string k, mixed v : props)
+  {
+    int reset_prop;
+    if (k[0] == VI_RESET_PREFIX)
+    {
+      reset_prop=1;
+      k=k[1..];
+    }
+    switch(k)
+    {
+      case P_READ_DETAILS:
+        if (reset_prop) ob->RemoveReadDetail(0);
+        walk_mapping(v, "AddReadDetail", ob);
+        break;
+      case P_DETAILS:
+        if (reset_prop) ob->RemoveDetail(0);
+        walk_mapping(v, "AddDetail", ob);
+        break;
+      case P_SMELLS:
+        if (reset_prop) ob->RemoveSmells(0);
+        walk_mapping(v, "AddSmells", ob);
+        break;
+      case P_SOUNDS:
+        if (reset_prop) ob->RemoveSounds(0);
+        walk_mapping(v, "AddSounds", ob);
+        break;
+      case P_TOUCH_DETAILS:
+        if (reset_prop) ob->RemoveTouchDetail(0);
+        walk_mapping(v, "AddTouchDetail", ob);
+        break;
+      case P_IDS:
+        if (reset_prop) ob->SetProp(P_IDS, v);
+        else ob->AddId(v);
+      case P_CLASS:
+        if (reset_prop) ob->SetProp(P_CLASS, v);
+        else ob->AddClass(v);
+      case P_ADJECTIVES:
+        if (reset_prop) ob->SetProp(P_ADJECTIVES, v);
+        else ob->AddAdjective(v);
+        break;
+
+      // Alle anderen Properties stumpf setzen.
+      default:
+        ob->SetProp(k, v);
+    }
+  }
+}
+
+// Clont ein vitem, falls noetig.
+// Nebeneffekt ist aber in jedem Fall auch, dass ein nicht mehr virtuell
+// anwesendes Objekt als VI_LAST_OBJ gespeichert und VI_OBJECT geloescht wird.
+private void check_vitem(string key, string path, int refresh,
+                         object obj, mapping props, mapping shadow_props,
+                         object last_obj)
+{
+  // Ist es noch da? Ein vItem ist "da", wenn obj auf ein gueltiges Objekt
+  // zeigt, welches *KEIN* Environment hat. (Hat es ein Environment, wurde es
+  // mitgenommen.)
+  if (obj)
+  {
+    if (!environment(obj))
+      return;
+    // wenn es mitgenommen wurde, ist obj in jedem Fall kein vItem mehr:
+    // (eigentlich wird das in VItemMoved schon gemacht, aber mal fuer den
+    // Fall, dass jemand den Shadow hart entsorgt hat o.ae.)
+    last_obj = obj;
+    obj = 0;
+    // und wird ggf. unten neu erzeugt.
+  }
+
+  if (refresh < 0)
+  {
+    object sh;
+    if (path)
+    {
+      obj=clone_object(path);
+      obj->SetAutoObject(1);
+      if (mappingp(props))
+        configure_object(obj, props);
+      // Schatten erzeugen, welcher die Beschreibung des Objekts im Container nach
+      // den Props in shadow_props abaendert.
+      sh = clone_object("/obj/vitem_shadow");
+      sh->set_shadow(obj, shadow_props);
+    }
+    else
+    {
+      obj=clone_object("/obj/vitem_proxy");
+      configure_object(obj, shadow_props);
+      // no shadow needed in this case.
+    }
+  }
+}
+
+// Erzeugt Instanzen der vItems (sofern noetig und erlaubt durch
+// Refresh-Parameter).
+private mixed CheckVItems()
+{
+  mixed vitems=QueryProp(P_VITEMS);
+  if (!mappingp(vitems))
+    vitems = m_allocate(0, VI_LENGTH);
+  walk_mapping(vitems, #'check_vitem);
+  return vitems;
+}
+
+// Liefert alle in diesem Raum virtuell anwesenden Items
+public object *GetVItemClones()
+{
+  mapping vitems = CheckVItems();
+  return filter(m_values(vitems, VI_OBJECT), #'objectp);
+}
+
+public object present_vitem(string complex_desc)
+{
+  foreach(object o : GetVItemClones())
+  {
+    if (o->id(complex_desc))
+      return o;
+  }
+  return 0;
+}
+
+// wird aus dem Shadow fuer das VItem gerufen, wenn es genomment etc. wird.
+// In dem Fall wird das Refresh "gesperrt", d.h. es wird fruehestens nach dem
+// naechsten Reset wieder neu erzeugt - sofern im naechsten Reset die
+// Vorraussetzungen erfuellt sind.
+public void VItemMoved(object ob)
+{
+  if (load_name(previous_object()) == "/obj/vitem_shadow")
+  {
+    mapping vitems = QueryProp(P_VITEMS);
+    if (!mappingp(vitems))
+      return;
+    // passendes vitem suchen
+    foreach(string key, string path, int refresh, object o, mapping props,
+            mapping shadow_props, object last_obj: &vitems)
+    {
+      if (ob != o)
+        continue;
+      else
+      {
+        // mitgenommenes Objekt merken und vitem-objekt loeschen
+        last_obj = o;
+        o = 0;
+        // Sperren gegen sofortiges Neuerzeugen, wenn refresh nicht
+        // VI_REFRESH_INSTANT ist.
+        if (refresh != VI_REFRESH_INSTANT)
+          refresh = negate(refresh);
+        break;
+      }
+    }
+  }
+}
+
+void reset()
+{
+  mapping vitems=QueryProp(P_VITEMS);
+  if (!mappingp(vitems))
+    return;
+  foreach(string key, string path, int refresh, object obj,
+          mapping props, mapping shadow_props, object last_obj : &vitems)
+  {
+    // Wenn negativ (d.h. vItem wurde noch nicht mitgenommen), 0 oder
+    // REFRESH_NONE, muss hier nix gemacht werden.
+    if (refresh <= REFRESH_NONE)
+      continue;
+
+    // Wenn last_obj nicht mehr existiert, darf (ausgenommen natuerlich
+    // REFFRESH_NONE) immer neu erzeugt werden.
+    if (!last_obj)
+    {
+      refresh=negate(refresh);
+      continue;
+    }
+
+    // restliche Faelle
+    switch(refresh)
+    {
+      case VI_REFRESH_MOVE_HOME:
+        // Wenn das Objekt nicht mehr als vItem hier ist (auch wenn es hier im
+        // Raum liegt!), wird es heim bewegt (sprich: zerstoert und neues
+        // vitem).
+        // (Da man hier nur hinkommt, wenn es mitgenommen wurde (refresh > 0),
+        // wird es immer refresht...
+        // Zu beachten: es soll auch nicht hier in diesem Container rumliegen
+        // nach dem Heimbewegen, also zerstoeren!
+        last_obj->remove(1);
+        // Fallthrough
+      case VI_REFRESH_REMOVE:
+        // wenn nicht mehr als vItem hier ist (d.h. auch wenn es hier im Raum
+        // rumliegt!) darf es neu erzeugt werden.
+        // (Hierher kommt die Ausfuehrung nur her, wenn es mitgenommen
+        // wurde, d.h. letztendlich: immer. d.h. Fallthrough.)
+      case VI_REFRESH_ALWAYS:
+        // neu erzeugen
+        refresh=negate(refresh);
+        break;
+    }
+  }
+}
+
diff --git a/std/npc.c b/std/npc.c
index 0ddd719..5cb7fcf 100644
--- a/std/npc.c
+++ b/std/npc.c
@@ -31,6 +31,7 @@
 inherit "/std/npc/info";
 inherit "/std/npc/put_and_get";
 inherit "/std/npc/items";
+inherit "/std/container/vitems";
 inherit "/std/thing/envchk";
 inherit "/std/living/helpers";
 
@@ -109,6 +110,7 @@
 
 void reset(){
   items::reset();
+  vitems::reset();
   combat::reset();
   envchk::reset();
 }
diff --git a/std/player/base.c b/std/player/base.c
index a4f271c..27c4e70 100644
--- a/std/player/base.c
+++ b/std/player/base.c
@@ -21,6 +21,7 @@
 
 inherit "/std/hook_provider";
 inherit "/std/player/restrictions";
+inherit "/std/player/vitems";
 inherit "/std/living/attributes";
 inherit "/std/living/put_and_get";
 inherit "/std/living/clothing";
diff --git a/std/player/vitems.c b/std/player/vitems.c
new file mode 100644
index 0000000..88c5e9b
--- /dev/null
+++ b/std/player/vitems.c
@@ -0,0 +1,16 @@
+// MorgenGrauen MUDlib
+//
+// player/vitems.c -- no vitems for players
+//
+#pragma strict_types,rtt_checks, range_check, pedantic
+#pragma no_clone
+
+protected object *GetVItemClones()
+{
+  return ({});
+}
+
+protected object present_vitem(string complex_desc)
+{
+  return 0;
+}
diff --git a/std/room.c b/std/room.c
index d87c0fd..718528a 100644
--- a/std/room.c
+++ b/std/room.c
@@ -20,6 +20,7 @@
 inherit "/std/room/exits";
 inherit "/std/room/commands";
 inherit "/std/room/items";
+inherit "/std/container/vitems";
 inherit "/std/room/doors";
 
 #include <thing/properties.h>
@@ -37,6 +38,7 @@
 void reset()
 {
   items::reset();
+  vitems::reset();
   exits::reset();
   doors::reset();
 }
diff --git a/std/tray.c b/std/tray.c
index 51319c6..2ee8511 100644
--- a/std/tray.c
+++ b/std/tray.c
@@ -23,6 +23,8 @@
 inherit "/std/thing/language";
 inherit "/std/container/light";
 inherit "/std/container/restrictions";
+inherit "/std/container/vitems";
+inherit "/std/container/items";
 inherit "/std/tray/description";
 inherit "/std/thing/envchk";
 inherit "/std/container/items";
@@ -52,5 +54,6 @@
 void reset()
 {
   items::reset();
+  vitems::reset();
   envchk::reset();
 }