Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/std/living/attributes.c b/std/living/attributes.c
new file mode 100644
index 0000000..90027a4
--- /dev/null
+++ b/std/living/attributes.c
@@ -0,0 +1,671 @@
+// MorgenGrauen MUDlib
+//
+// living/attributes.c -- attributes for living objects
+//
+// $Id: attributes.c 8375 2013-02-12 21:52:58Z Zesstra $
+
+#include <sys_debug.h>
+
+// attribute handling
+//
+//   filter_ldfied: str, dex, con, int
+//
+// In den Attributen und Abilites werden Rassenspzifische und erworbene
+// Faehigkeiten (mit autoload-Eigenschaft) abgepseichert.
+//
+// Funktionen:
+//   SetAttribute( attr, val ) (Erzeuge und) setze das Attribut auf val.
+//   QueryAttribute( attr )    Gebe den Wert des Attributes zurueck.
+//
+//   Wenn ein Objekt eine Funktion _filterattr_<name> beitzt, wird beim Setzen
+//   des Attributes <name>, der vorgeschlagene Wert uebergeben und der von
+//   dieser Funktion zurueckgegebene gesetzt. (fuer ueberpruefungszwecke)
+//   Gleiches gilt fuer _filterabil_<name>.
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <attributes.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+
+mapping attributes; // Dies sind die mit ZTs veraenderbaren Attribute
+mapping attributes_modifier; // Modifier sollen gespeichert werden
+nosave mixed* attributes_timed_mods; //P_TIMED_ATTR_MOD
+nosave mapping attributes_offsets; // Offsets NICHT speichern!
+nosave mapping used_attributes_offsets; // Zur Beschleunigung der Berechnung
+nosave object* all_modifiers; // objekte mit P_X/M_ATTR_MOD, P_X/M_HEALTH_MOD
+nosave object* invalid_modifiers; // objekte welche das limit ueberschreiten
+nosave int cumulative_mod; // bilanz der P_X/M_ATTR_MOD
+nosave int hp_off;
+nosave int sp_off;
+
+
+nomask public int SetTimedAttrModifier(string key, mapping modifier, 
+    int outdated, object dependent, mixed notify) {
+  if(!key || key=="" || !modifier || (outdated>0 && outdated<time()) || 
+      (notify && !stringp(notify) && !objectp(notify)) ) {
+    return TATTR_INVALID_ARGS;
+  }
+  
+  
+  if(member(attributes_timed_mods[TATTR_ENTRIES],key)) {
+    // change entry
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_MOD]=modifier;
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_OUTDATED]=outdated;
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_DEPENDENT]=dependent;
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_NOTIFY]=notify;
+  }
+  else {
+    // add entry
+    attributes_timed_mods[TATTR_ENTRIES]+=([key:modifier;outdated;dependent;notify]);
+  }
+  
+  // add outdate
+  if(outdated>0 && member(attributes_timed_mods[TATTR_OUTDATE],outdated)==-1)
+  {
+    attributes_timed_mods[TATTR_OUTDATE]+=({outdated});
+    attributes_timed_mods[TATTR_OUTDATE]=
+      sort_array(attributes_timed_mods[TATTR_OUTDATE],#'<);
+  }
+  
+  // add dependent
+  if(objectp(dependent))
+  {
+  	if(member(attributes_timed_mods[TATTR_DEPENDENTS],key))
+  	{
+	  attributes_timed_mods[TATTR_DEPENDENTS][key]=dependent;
+  	}
+  	else
+  	{
+          attributes_timed_mods[TATTR_DEPENDENTS]+=([key:dependent]);
+  	}
+  }
+  else
+  {
+  	// remove previously set dependent
+  	if(member(attributes_timed_mods[TATTR_DEPENDENTS],key))
+  	{
+	  attributes_timed_mods[TATTR_DEPENDENTS]-=([key]);
+  	}
+  }
+  
+  UpdateAttributes();
+
+  return TATTR_OK;
+}
+
+nomask public mapping QueryTimedAttrModifier(string key)
+{
+  int outdated;
+  object dependent;
+  mixed notify;
+  mapping mod;
+  
+  if(!key || key=="")
+  {
+    return ([]);
+  }
+
+  if(!member(attributes_timed_mods[TATTR_ENTRIES],key))
+  { 
+    return ([]);
+  }
+
+  mod=deep_copy(attributes_timed_mods[TATTR_ENTRIES][key,TATTR_MOD]);
+  outdated=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_OUTDATED];
+  dependent=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_DEPENDENT];
+  notify=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_NOTIFY];
+  
+  return ([key:mod;outdated;dependent;notify ]);
+}
+
+nomask public int DeleteTimedAttrModifier(string key)
+{
+  if(!key || key=="")
+  {
+    return TATTR_INVALID_ARGS;
+  }
+
+  if(!member(attributes_timed_mods[TATTR_ENTRIES],key))
+  { 
+    return TATTR_NO_SUCH_MODIFIER;
+  }
+  
+  attributes_timed_mods[TATTR_DEPENDENTS]-=([key]);
+  attributes_timed_mods[TATTR_ENTRIES]-=([key]);
+  UpdateAttributes();
+  
+  return TATTR_OK;
+}
+
+nomask protected void attribute_hb()
+{
+  int now,i,k,update,outdated;
+  string* keys;
+  mapping tonotify;
+	
+  // initialize
+  now=time();
+  tonotify=([]);
+
+  keys=m_indices(attributes_timed_mods[TATTR_ENTRIES]);
+  
+  // delete outdated 
+  for(i=sizeof(attributes_timed_mods[TATTR_OUTDATE])-1;i>=0;i--)
+  {
+  	outdated=attributes_timed_mods[TATTR_OUTDATE][i];
+  	if(outdated>now)
+  	{
+  	  break;
+  	}
+  	
+  	for(k=sizeof(keys)-1;k>=0;k--)
+  	{
+  	  if(attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_OUTDATED]==outdated)
+  	  {
+  	    // bei fehlendem notifier wurde das zum verhaengnis
+  	    // dank an gloinson
+  	    /*
+  	    if(objectp(attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_NOTIFY]))
+  	    {
+  	    */
+  	    tonotify+=([keys[k]:attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_NOTIFY]]);
+  	    //}
+  	    
+	    attributes_timed_mods[TATTR_DEPENDENTS]-=([keys[k]]);
+ 	    attributes_timed_mods[TATTR_ENTRIES]-=([keys[k]]);
+  	    keys-=({keys[k]});
+  	  }
+  	}
+  	
+  	attributes_timed_mods[TATTR_OUTDATE]-=({outdated});
+  }
+  
+  // delete depending
+  keys=m_indices(attributes_timed_mods[TATTR_DEPENDENTS]);
+  for(i=sizeof(keys)-1;i>=0;i--)
+  {
+  	if(!objectp(attributes_timed_mods[TATTR_DEPENDENTS][keys[i]]))
+  	{
+  	    // siehe oben
+  	    /*
+  	    if(objectp(attributes_timed_mods[TATTR_ENTRIES][keys[i],TATTR_NOTIFY]))
+  	    {
+  	    */
+  	    tonotify+=([keys[i]:attributes_timed_mods[TATTR_ENTRIES][keys[i],TATTR_NOTIFY]]);
+  	    //}
+
+	    attributes_timed_mods[TATTR_DEPENDENTS]-=([keys[i]]);
+ 	    attributes_timed_mods[TATTR_ENTRIES]-=([keys[i]]);
+  	    keys-=({keys[i]});
+  	}
+  }
+  
+  
+  // update
+  if(sizeof(tonotify))
+  {
+    UpdateAttributes();
+    call_out(#'notifyExpiredModifiers,0,tonotify);
+  }
+}
+
+nomask protected void notifyExpiredModifiers(mapping nots)
+{
+  int i;
+  string* keys;
+  while(remove_call_out(#'notifyExpiredModifiers)!=-1);
+  
+  if(!nots)
+  {
+    return;
+  }
+  
+  keys=m_indices(nots);
+  for(i=sizeof(nots)-1;i>=0;i--)
+  {
+  	if(nots[keys[i]] && 
+	    ( objectp(nots[keys[i]])||stringp(nots[keys[i]]) ) )
+  	{
+  	   call_other(nots[keys[i]],"NotifyTimedAttrModExpired",keys[i]);
+  	}
+  }
+}
+
+
+// invalide modifier benachrichtigen
+nomask protected void notifyInvalidModifiers() {
+
+  while(remove_call_out(#'notifyInvalidModifiers)!=-1);
+  
+  if(!invalid_modifiers) {
+    invalid_modifiers=({});
+  }
+  
+  call_other(invalid_modifiers,"NotifyXMAttrModLimitViolation");
+
+}
+
+
+// welche Modifier / modif. Objekte wirken nun?
+protected nomask void calculate_valid_modifiers() {
+  closure qp;
+  mapping res;	
+  string key;	
+  int wert;
+  // Unterscheidung Bonus <-> Malus, weil der Malus voll eingehen soll	
+  int hp_malus, sp_malus;
+ 
+  used_attributes_offsets=([]);
+  cumulative_mod=0;
+  hp_off=sp_off=0;
+  invalid_modifiers=({});
+
+  // rassenspezifische boni P_ATTRIBUTES_OFFSETS  
+  if ( mappingp(attributes_offsets) )
+    used_attributes_offsets+=attributes_offsets;
+
+  if (!pointerp(all_modifiers) || !sizeof(all_modifiers)) {
+    // in diesem Fall koennen wir hier direkt mit dieser Funktion Schluss
+    // machen. ;-)
+    return;
+  }
+  else
+    all_modifiers-=({0}); //zerstoerte Objekte rauswerfen.
+    
+  // einmal ueber alle modifizierenden Objekt iterieren und aufaddieren
+  foreach(object ob: all_modifiers) {
+    qp = symbol_function("QueryProp",ob);
+ 
+    if (!objectp(ob) || environment(ob)!=this_object()) {
+      all_modifiers-=({ob});
+      continue;
+    }
+ 		
+    // ext. Attribut-Modifier
+    if ( mappingp(res=funcall(qp,P_X_ATTR_MOD)) ) {		
+      foreach(key, wert: res) {
+        cumulative_mod+=wert;
+      }
+    }
+
+    // magic Attribut-Modifier
+    if ( mappingp(res=funcall(qp,P_M_ATTR_MOD))
+        && ( funcall(qp,P_WORN)
+            || funcall(qp,P_WIELDED) ) ) {		
+      foreach(key, wert: res) {
+        cumulative_mod+=wert;
+      }
+    }
+    // Magic Health-Modifier 		
+    if ( mappingp(res=funcall(qp,P_M_HEALTH_MOD))
+        && ( funcall(qp,P_WORN)
+            || funcall(qp,P_WIELDED) ) ) {
+      if (res[P_HP] < 0)
+        hp_malus += res[P_HP];
+      else
+        hp_off+=res[P_HP];
+
+      if (res[P_SP] < 0)
+        sp_malus += res[P_SP];
+      else
+        sp_off+=res[P_SP];
+      }
+	      
+    // external Health Modifier
+    if ( mappingp(res=funcall(qp,P_X_HEALTH_MOD)) ) {
+      if (res[P_HP] < 0)
+        hp_malus += res[P_HP];
+      else
+        hp_off+=res[P_HP];
+
+      if (res[P_SP] < 0)
+        sp_malus += res[P_SP];
+      else
+        sp_off+=res[P_SP];	    
+    }	
+  
+  } // Ende 1. foreach()
+  
+ 
+  // und nochmal, soviele Objekt wieder rausschmeissen, bis das Limit wieder
+  // unterschritten wird. (Verbesserungsvorschlaege erwuenscht.) :-(
+  foreach(object ob: all_modifiers) {
+
+    qp = symbol_function("QueryProp",ob);
+ 
+    if ( mappingp(res=funcall(qp,P_X_ATTR_MOD)) ) {
+      if(cumulative_mod>CUMULATIVE_ATTR_LIMIT) { 
+        invalid_modifiers+=({ob});	  
+	foreach(key, wert: res) {
+          cumulative_mod-=wert;
+        }
+      }
+      else {
+        add_offsets(res);
+      }
+    }
+
+    if ( mappingp(res=funcall(qp,P_M_ATTR_MOD))
+        && ( funcall(qp,P_WORN)
+            || funcall(qp,P_WIELDED) ) ) {
+      if(cumulative_mod>CUMULATIVE_ATTR_LIMIT) {     
+        if(member(invalid_modifiers,ob)==-1) {
+          invalid_modifiers+=({ob});
+        }			
+	foreach(key, wert: res) {
+          cumulative_mod-=wert;
+        }
+      }
+      else {
+        add_offsets(res);
+      }
+    }
+  }
+
+  // HEALTH_MOD werden durch eine Formel 'entschaerft', damit man nur schwer
+  // das Maximum von 150 erreichen kann. (Formel von Humni, beschlossen auf 3.
+  // EM-Treffen)
+  // Mali gehen aber voll ein
+  hp_off = (int)(150 - (150*150.0/(150 + hp_off))) + hp_malus;
+  sp_off = (int)(150 - (150*150.0/(150 + sp_off))) + sp_malus;
+
+  /* alte Version
+    hp_off += hp_malus;
+    sp_off += sp_malus;
+  */
+ 
+  // notify invalid modifiers
+  if(sizeof(invalid_modifiers)>0) {
+    call_out(#'notifyInvalidModifiers,0);
+  }
+}
+
+// abmelden eines modifiers 
+nomask public void deregister_modifier(object modifier)
+{ 
+  if (!pointerp(all_modifiers)) {
+    return;
+  }
+
+  // valid object?
+  if (!objectp(modifier) || member(all_modifiers,modifier)==-1) {
+    return;
+  }
+  
+  all_modifiers-=({modifier});
+  if (invalid_modifiers) {
+    invalid_modifiers-=({modifier});
+  }
+}
+
+// anmelden eines modifiers 
+nomask public void register_modifier(object modifier) { 
+  closure qp;
+  
+  if (!pointerp(all_modifiers)) {
+    all_modifiers=({});
+  }
+
+  // valid object?
+  if (!objectp(modifier) || environment(modifier)!=this_object() || 
+      member(all_modifiers,modifier)!=-1) {
+    return;
+  }
+  
+  qp = symbol_function("QueryProp",modifier);
+  // modifier after all? Die P_M_* muessen getragen/gezueckt sein.
+  if(mappingp(funcall(qp,P_X_ATTR_MOD)) ||
+     mappingp(funcall(qp,P_X_HEALTH_MOD)) ||
+    ((mappingp(funcall(qp,P_M_ATTR_MOD)) ||
+       mappingp(funcall(qp,P_M_HEALTH_MOD))) &&
+      (funcall(qp,P_WORN) || funcall(qp,P_WIELDED)) ) ) {
+    all_modifiers+=({modifier});
+  }
+}
+
+protected void add_offsets(mapping arr) {
+  mixed *ind;
+  int i;
+  
+  if ( !mappingp(arr) )
+    return;
+
+  foreach(string key, int wert: arr) {
+    used_attributes_offsets[key]+=wert;
+  }
+}
+
+public void UpdateAttributes() {
+  mixed   *ind;
+  int     i;
+  
+  // alle gueltigen Modifier ermitteln aus Objekten im Inventar.
+  calculate_valid_modifiers();
+
+  // persistente modifier drauf (frosch zb)
+  // aus P_ATTRIBUTES_MODIFIERS
+  if ( mappingp(attributes_modifier) ) {
+    foreach(mixed key, mapping wert: attributes_modifier) {
+      add_offsets(wert); // Modifier addieren...		
+    }
+  }
+  // timed modifier drauf aus P_TIMED_ATTR_MOD
+  ind=m_indices(attributes_timed_mods[TATTR_ENTRIES]);
+  for ( i=sizeof(ind)-1 ; i>=0 ; i-- ) {
+    // Modifier addieren...
+    add_offsets(attributes_timed_mods[TATTR_ENTRIES][ind[i],0]);
+  }
+  // Bei Monstern werden die HP/SP ueblicherweise selbst gesetzt
+  if ( !query_once_interactive(this_object()))
+    return;
+
+  SetProp(P_MAX_HP, QueryAttribute(A_CON)*8+42+hp_off);
+  SetProp(P_MAX_SP, QueryAttribute(A_INT)*8+42+sp_off);
+  
+  if(QueryProp(P_HP)>QueryProp(P_MAX_HP)) {
+    SetProp(P_HP,QueryProp(P_MAX_HP));
+  }
+  
+  if(QueryProp(P_SP)>QueryProp(P_MAX_SP)) {
+    SetProp(P_SP,QueryProp(P_MAX_SP));
+  }
+
+  GMCP_Char( ([ A_INT: QueryAttribute(A_INT),
+                A_DEX: QueryAttribute(A_DEX),
+                A_STR: QueryAttribute(A_STR),
+                A_CON: QueryAttribute(A_CON) ]) );
+}
+
+
+protected void create() {
+  hp_off=sp_off=0;
+  used_attributes_offsets=([]);
+  all_modifiers=({});
+  invalid_modifiers=({});
+  cumulative_mod=0;
+  
+  Set(P_ATTRIBUTES_MODIFIER, attributes_modifier=([])); // nicht geschuetzt
+  Set(P_ATTRIBUTES_OFFSETS, attributes_offsets=([]));
+  Set(P_ATTRIBUTES_OFFSETS, PROTECTED, F_MODE);
+  Set(P_ATTRIBUTES, attributes=([]));
+  Set(P_ATTRIBUTES, PROTECTED, F_MODE);
+
+  Set(P_TIMED_ATTR_MOD, attributes_timed_mods=({ ({}),([]),([]) }));
+  Set(P_TIMED_ATTR_MOD, NOSETMETHOD|SECURED, F_MODE_AS);
+}
+
+static mixed _query_timed_attr_mod() {
+  mixed ret;
+  return Set(P_TIMED_ATTR_MOD,	    
+      ({attributes_timed_mods[0],
+	deep_copy(attributes_timed_mods[1]),
+	deep_copy(attributes_timed_mods[2])}));
+}
+
+static mapping _set_attributes(mapping arr) 
+{
+  Set(P_ATTRIBUTES, attributes=arr);
+  UpdateAttributes();
+  return arr;
+}
+
+static mapping _query_attributes() 
+{
+  return deep_copy(Set(P_ATTRIBUTES, attributes)); 
+}
+
+static mapping _set_attributes_offsets(mapping arr) 
+{
+  Set(P_ATTRIBUTES_OFFSETS, attributes_offsets=arr);
+  UpdateAttributes();
+  return attributes_offsets;
+}
+
+static mapping _query_attributes_offsets() 
+{ 
+  return deep_copy(Set(P_ATTRIBUTES_OFFSETS, attributes_offsets));
+}
+
+static mixed _set_attributes_modifier(mixed arr) 
+{ string fn;
+  mixed pre;
+  mapping map_ldfied;
+  
+  if ( pointerp(arr) && (sizeof(arr)>=2) )
+  {
+    pre=arr[0];
+    map_ldfied=arr[1];
+  }
+  else 
+  {
+    pre=previous_object();
+    map_ldfied=arr;
+  }
+
+  if ( objectp(pre) )
+    fn=old_explode(object_name(pre),"#")[0];
+  else
+    fn=pre;
+
+  if ( !stringp(fn) )
+    return 0;
+
+  // wenn Modifier kein mapping oder ein leeres Mapping: loeschen
+  if ( !mappingp(map_ldfied) || !sizeof(map_ldfied))
+    m_delete(attributes_modifier,fn);
+  else
+    attributes_modifier[fn]=map_ldfied;
+
+  Set(P_ATTRIBUTES_MODIFIER, attributes_modifier);
+  UpdateAttributes();
+  return attributes_modifier[fn];
+}
+
+static mapping _query_attributes_modifier() 
+{
+  return deep_copy(attributes_modifier);  
+}
+
+public int SetAttr(string attr, int val) 
+{ closure filter_ldfied;
+
+  if ( filter_ldfied = symbol_function("_filterattr_"+attr, this_object()) ) 
+    val = funcall(filter_ldfied, val );
+
+  attributes[attr] = val;
+  UpdateAttributes();
+  return val;
+}
+
+public int SetAttribute(string attr, int val) 
+{
+  return SetAttr(attr, val-used_attributes_offsets[attr]);
+}
+
+// Diese Funktion sollte zum Abfragen verwendet werden:
+public int QueryAttribute(string attr) 
+{ int re;
+
+  re=attributes[attr]+used_attributes_offsets[attr];
+
+  if ( query_once_interactive(this_object()) && (re>30) )
+    re=30;
+
+  return re;
+}
+
+public int QueryRealAttribute(string attr) 
+{
+  return attributes[attr];
+}
+
+public int SetRealAttribute(string attr, int val) 
+{
+  return SetAttr(attr, val);
+}
+
+public int QueryAttributeOffset(string attr) 
+{
+  return used_attributes_offsets[attr];
+}
+
+public status TestLimitViolation(mapping check)
+{
+  int k,test;
+  mixed* ind;
+    
+  if(!check || !mappingp(check))
+  {
+    return 0;
+  }
+
+  test=0;
+  ind=m_indices(check);
+  for( k=sizeof(ind)-1 ; k>=0 ; k-- )
+  {
+    test+=check[ind[k]];
+  }
+  
+  test+=cumulative_mod;
+  if(test>CUMULATIVE_ATTR_LIMIT)
+  {
+    return 1;
+  }
+  
+  return 0;
+}
+
+/*
+ *------------------------------------------------------------
+ * attributes compatibility functions
+ */
+
+protected int _filterattr_str(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
+
+protected int _filterattr_dex(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
+
+protected int _filterattr_int(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
+
+protected int _filterattr_con(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
diff --git a/std/living/clothing.c b/std/living/clothing.c
new file mode 100644
index 0000000..56aff44
--- /dev/null
+++ b/std/living/clothing.c
@@ -0,0 +1,96 @@
+// MorgenGrauen MUDlib
+//
+// living/clothing.c -- Modul fuer Bekleidungsfragen. ;-)
+//
+// $Id: armour.c,v 3.8 2003/08/25 09:36:04 Rikus Exp $
+#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 <living/clothing.h>
+#include <living/combat.h>
+#include <armour.h>
+
+protected void create() {
+  SetProp(P_CLOTHING, ({}));
+  Set(P_CLOTHING, PROTECTED, F_MODE_AS);
+}
+
+public object *FilterClothing(closure filterfun, varargs mixed* extra) {
+  if (!closurep(filterfun))
+    return ({});
+  return apply(#'filter, QueryProp(P_CLOTHING), filterfun, extra);
+}
+
+public object *FilterArmours(closure filterfun, varargs mixed* extra) {
+  if (!closurep(filterfun))
+    return ({});
+  return apply(#'filter, QueryProp(P_ARMOURS)-({0}), filterfun, extra);
+}
+
+public int WearClothing(object ob) {
+  object *clothing = QueryProp(P_CLOTHING);
+  if (member(clothing, ob) != -1)
+    return 0;
+  clothing += ({ob});
+  SetProp(P_CLOTHING, clothing);
+  return 1;
+}
+
+public int WearArmour(object ob) {
+  if (!VALID_ARMOUR_TYPE(ob->QueryProp(P_ARMOUR_TYPE)))
+    return 0;
+
+  object *armours = QueryProp(P_ARMOURS);
+  if (member(armours, ob) != -1)
+    return 0;
+
+  armours += ({ob});
+  SetProp(P_ARMOURS, armours);
+  return 1;
+}
+
+public int Wear(object ob) {
+  // reihenfolge ist wichtig! Ruestung sind _auch_ Kleidung, aber Kleidung
+  // keine Ruestung.
+  if (ob->IsArmour())
+    return WearArmour(ob);
+  else if (ob->IsClothing())
+    return WearClothing(ob);
+  return 0;
+}
+
+public int UnwearClothing(object ob) {
+object *clothing = QueryProp(P_CLOTHING);
+  if (member(clothing, ob) == -1)
+    return 0;
+  clothing -= ({ob});
+  SetProp(P_CLOTHING, clothing);
+  return 1;
+}
+
+public int UnwearArmour(object ob) {
+  object *armours = QueryProp(P_ARMOURS);
+  if (member(armours, ob) == -1)
+    return 0;
+
+  armours -= ({ob});
+  SetProp(P_ARMOURS, armours);
+  return 1;
+}
+
+public int Unwear(object ob) {
+  // reihenfolge ist wichtig! Ruestung sind _auch_ Kleidung, aber Kleidung
+  // keine Ruestung.
+  if (ob->IsArmour())
+    return UnwearArmour(ob);
+  else if (ob->IsClothing())
+    return UnwearClothing(ob);
+  return 0;
+}
+
diff --git a/std/living/combat.c b/std/living/combat.c
new file mode 100644
index 0000000..880f3f6
--- /dev/null
+++ b/std/living/combat.c
@@ -0,0 +1,2311 @@
+// MorgenGrauen MUDlib
+//
+// living/combat.c -- Basis-Kampfmodul
+//
+// $Id: combat.c 9568 2016-06-05 18:53:10Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/skill_utils";
+inherit "/std/living/inventory";
+inherit "/std/living/team";
+
+#include <sys_debug.h>
+#include <debug_message.h>
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#include <living/skills.h>
+#include <thing/properties.h>
+#include <player/comm.h>
+#include <living/skill_attributes.h>
+#include <combat.h>
+#include <living.h>
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <attributes.h>
+#include <new_skills.h>
+
+#include <defines.h>
+
+#include <sensitive.h>
+
+#define HUNTTIME 300 //300 HBs sind 10 Minuten
+#define RNAME(x) capitalize(getuid(x))
+
+// 'private'-Prototypes
+private string _kill_alias( string str );
+
+// globale Variablen
+nosave mapping enemies;
+private nosave string magic_attack;
+private nosave int attack_busy;
+nosave int no_more_attacks;
+private nosave int remaining_heart_beats;
+private nosave int att2_time;
+private nosave string last_attack_msg;
+private nosave object *missing_attacks;
+private nosave mapping peace_tries;
+// Cache fuer QueryArmourByType()
+private nosave mapping QABTCache;
+
+protected void create()
+{
+  Set(P_WIMPY, SAVE, F_MODE_AS);
+  Set(P_TOTAL_AC, PROTECTED, F_MODE_AS);
+  Set(P_HANDS, SAVE, F_MODE_AS);
+  Set(P_SHOW_ATTACK_MSG,SAVE,F_MODE_AS);
+  Set(P_RESISTANCE, ({}));
+  Set(P_VULNERABILITY, ({}));
+  Set(P_GUILD_PREPAREBLOCK, SAVE, F_MODE_AS);
+  // Kein Setzen von P_ARMOURS von aussen per Set(). (Per SetProp() geht es
+  // durch die Setmethode).
+  Set(P_ARMOURS,PROTECTED,F_MODE_AS);
+  SetProp(P_ARMOURS, ({}));
+  attack_busy=100;
+  att2_time=0;
+  enemies=([]);
+  peace_tries=([]);
+  team::create(); 
+  offerHook(H_HOOK_DEFEND,1);
+  offerHook(H_HOOK_ATTACK,1);
+  offerHook(H_HOOK_ATTACK_MOD,1);
+}
+
+#undef DEBUG
+#define DEBUG(x) if (find_object("zesstra")) \
+  tell_object(find_player("zesstra"),x)
+
+public void UpdateResistanceStrengths() { 
+  mapping resmods, strmap;
+
+  if ( !mappingp(resmods=Query(P_RESISTANCE_MODIFIER)) )
+    return;
+
+  //erstmal die alte Aufsummation loeschen
+  m_delete(resmods,"me");
+
+  //wenn jetzt leer: Abbruch, keine Res-Modifier da.
+  if (!sizeof(resmods))
+      return(Set(P_RESISTANCE_MODIFIER,0));
+
+  strmap = ([]);
+
+  // ueber alle gesetzten ResModifier gehen
+  foreach(string mod, mapping resmap, object ob: resmods) {
+    if ( !mappingp(resmap) || !sizeof(resmap)
+        || !objectp(ob) ) {
+      m_delete(resmods, mod);
+      continue; // Resi ungueltig, weg damit.
+    }
+    // jetzt noch ueber die Submappings laufen, die die Resis der Objekte
+    // beinhalten.
+    foreach(string reskey, float resi: resmap) {
+        strmap[reskey] = ((strmap[reskey]+1.0)*(resi+1.0))-1.0;
+    }
+  }
+
+  if ( !sizeof(strmap) )
+    Set(P_RESISTANCE_MODIFIER, 0);
+  else
+    Set(P_RESISTANCE_MODIFIER, resmods+([ "me" : strmap; 0 ]) );
+}
+
+public varargs int AddResistanceModifier(mapping mod, string add)
+{ string  key;
+  mapping res;
+
+  if ( !mappingp(mod) || !sizeof(mod) || !previous_object() )
+    return 0;
+
+  key = explode(object_name(previous_object()),"#")[0];
+
+  if ( add )
+    key += ("#"+add);
+
+  res = Query(P_RESISTANCE_MODIFIER);
+  mod = deep_copy(mod);
+
+  if ( !mappingp(res) )
+    res = ([ key : mod; previous_object() ]);
+  else
+    res += ([ key : mod; previous_object() ]);
+
+  Set(P_RESISTANCE_MODIFIER, res);
+  UpdateResistanceStrengths();
+
+  return 1;
+}
+
+public varargs void RemoveResistanceModifier(string add)
+{ string  key;
+  mapping res;
+
+  if ( !previous_object() )
+    return;
+
+  key = explode(object_name(previous_object()),"#")[0];
+
+  if ( add )
+    key += ("#"+add);
+
+  if ( !mappingp(res = Query(P_RESISTANCE_MODIFIER)) )
+    return;
+
+  m_delete(res, key);
+  Set(P_RESISTANCE_MODIFIER, res);
+  UpdateResistanceStrengths();
+}
+
+// veraltete Prop, aus Kompatibilitaetsgruenden noch vorhanden.
+static mixed _set_resistance(mixed arg)
+{ int     i;
+  mapping resimap;
+  mixed   old;
+
+  if ( !pointerp(arg) )
+    arg=({arg});
+
+  if ( !mappingp(resimap=Query(P_RESISTANCE_STRENGTHS)) )
+    resimap=([]);
+
+  if ( pointerp(old=QueryProp(P_RESISTANCE)) )
+    for ( i=sizeof(old)-1 ; i>=0 ; i-- )
+      resimap[old[i]]=(1.0+((float)resimap[old[i]]))*2.0-1.0;
+
+  for ( i=sizeof(arg)-1 ; i>=0 ; i-- )
+    resimap[arg[i]]=(1.0+((float)resimap[arg[i]]))*0.5-1.0;
+
+  SetProp(P_RESISTANCE_STRENGTHS,resimap);
+
+  return Set(P_RESISTANCE,arg);
+}
+
+// veraltete Prop, aus Kompatibilitaetsgruenden noch vorhanden.
+static mixed _set_vulnerability(mixed arg)
+{ int     i;
+  mapping resimap;
+  mixed   old;
+
+  if ( !pointerp(arg) )
+    arg=({arg});
+
+  if ( !mappingp(resimap=Query(P_RESISTANCE_STRENGTHS)) )
+    resimap=([]);
+
+  if ( pointerp(old=QueryProp(P_VULNERABILITY)) )
+    for ( i=sizeof(old)-1 ; i>=0 ; i-- )
+      resimap[old[i]]=(1.0+((float)resimap[old[i]]))*0.5-1.0;
+
+  for ( i=sizeof(arg)-1 ; i>=0 ; i-- )
+    resimap[arg[i]]=(1.0+((float)resimap[arg[i]]))*2.0-1.0;
+
+  SetProp(P_RESISTANCE_STRENGTHS,resimap);
+
+  return Set(P_VULNERABILITY,arg);
+}
+
+
+/** kill - Kampf starten.
+ * Fuegt ob der Feindesliste hinzu.
+ */
+public int Kill(object ob)
+{ int   res, arena;
+  int|string no_attack;
+
+  if ( !objectp(ob) )
+    return 0;
+
+  if ( ob->QueryProp(P_GHOST) )
+  {
+    tell_object(ME,ob->Name(WER)+" ist doch schon tot!\n");
+    return -1;
+  }
+
+  if ( no_attack = ob->QueryProp(P_NO_ATTACK) )
+  {
+    if ( stringp(no_attack) )
+      tell_object(ME, no_attack);
+    else
+      tell_object(ME, ob->Name(WER,1)+" laesst sich nicht angreifen!\n");
+
+    return -2;
+  }
+
+  if ( QueryProp(P_NO_ATTACK) )
+    return -3;
+
+  res=InsertEnemy(ob);
+  if (res)
+    tell_object(ME, "Ok.\n");
+  else if (IsEnemy(ob))
+    tell_object(ME, "Jajaja, machst Du doch schon!\n");
+  //else //kein gueltiger Feind, ob wurde nicht eingetragen.
+
+  if ( !res )
+    return -4;  // nicht aendern ohne Kill in player/combat.c
+
+  return 1;
+}
+
+public int InsertSingleEnemy(object ob)
+{
+    if ( !living(ob) )
+        return 0;
+
+    // Wie lange verfolgt dieses Living? Wenn Prop nicht gesetzt oder 
+    // ungueltig, d.h. kein int oder <= 0, dann wird die Defaultzeit genommen.
+    int hunttime = (int)QueryProp(P_HUNTTIME);
+    hunttime = (intp(hunttime) && hunttime > 0) ? 
+                 hunttime / __HEART_BEAT_INTERVAL__ : HUNTTIME;
+
+    // Auch wenn ein Objekt schon als Gegner eingetragen ist, trotzdem die
+    // HUNTTIME erneuern. Das "return 0;" muss aber bleiben, da der Gegner
+    // ja nicht neu ist.
+    //
+    // 09.12.2000, Tiamak
+    if ( member( enemies, ob ) ) {
+        enemies[ob,ENEMY_HUNTTIME] = hunttime;
+        return 0;
+    }
+
+    if ( (ob==ME) || QueryProp(P_NO_ATTACK)
+         || !objectp(ob) || (ob->QueryProp(P_NO_ATTACK))
+         || (ob->QueryProp(P_GHOST)) )
+        return 0;
+
+    object team;
+    if ( ( query_once_interactive(ME) || query_once_interactive(ob) )
+         && objectp(team=Query(P_TEAM)) && (team==(ob->Query(P_TEAM))) )
+        return 0;
+
+    set_heart_beat(1);
+
+    last_attack_msg=0;
+
+    enemies[ob,ENEMY_HUNTTIME] = hunttime;
+    Set(P_LAST_COMBAT_TIME,time());
+
+    return 1;
+}
+
+public int InsertEnemy(object ob)
+{ int res;
+
+  if ( res=InsertSingleEnemy(ob) )
+    InsertEnemyTeam(ob);
+
+  return res;
+}
+
+public object QueryPreferedEnemy()
+{ int    sz,r;
+  <int|object>*  pref;
+  object enemy;
+
+  enemy=0;
+  if ( pointerp(pref=QueryProp(P_PREFERED_ENEMY)) && ((sz=sizeof(pref))>1)
+      && intp(r=pref[0]) && (random(100)<r) )
+  {
+    enemy=pref[1+random(sz-1)];
+
+    if ( !objectp(enemy) )
+    {
+      pref-=({enemy});
+
+      if ( sizeof(pref)<2 )
+        pref=0;
+
+      SetProp(P_PREFERED_ENEMY,pref);
+
+      return 0;
+    }
+
+    if ( !IsEnemy(enemy) )
+      return 0;
+  }
+
+  return enemy;
+}
+
+public object *PresentEnemies() {
+
+  object *here=({});
+  object *netdead=({});
+  mixed no_attack;
+  foreach(object pl: enemies) {
+    if (no_attack = (mixed)pl->QueryProp(P_NO_ATTACK)) {
+      m_delete(enemies,pl);
+      // Und auch im Gegner austragen, sonst haut der weiter zu und dieses
+      // Living wehrt sich nicht. (Zesstra, 26.11.2006)
+      if (stringp(no_attack)) {
+        tell_object(ME, no_attack);
+        pl->StopHuntFor(ME, 1);
+      }
+      else pl->StopHuntFor(ME, 0);
+    }
+    else if ( environment()==environment(pl) )
+    {
+      if ( interactive(pl) || !query_once_interactive(pl) )
+        here+=({pl});
+      else
+        netdead+=({pl});
+    }
+  }
+
+  if ( !sizeof(here) ) // Netztote sind nur Feinde, falls keine anderen da sind
+    return netdead;
+
+  return here;
+}
+
+public varargs object SelectEnemy(object *here)
+{ object enemy;
+
+  if ( !pointerp(here) )
+    here=PresentEnemies();
+
+  if ( !sizeof(here) )
+    return 0;
+
+  if ( !objectp(enemy=QueryPreferedEnemy())
+      || (environment(enemy)!=environment()) )
+    enemy=here[random(sizeof(here))];
+
+  return enemy;
+}
+
+protected void heart_beat() {
+  int hbs; 
+  // leider rufen viele Leute einfach das geerbte heart_beat() obwohl sie
+  // schon tot sind und damit das Objekt zerstoert ist.
+  if (!living(this_object()))
+      return;
+
+  // Paralyse pruefen, ggf. reduzieren
+  int dis_attack = QueryProp(P_DISABLE_ATTACK);
+  if ( intp(dis_attack) && (dis_attack > 0) )
+  {
+      SetProp(P_DISABLE_ATTACK, --dis_attack);
+  }
+
+  // Attacken ermitteln: SA_SPEED + Rest aus dem letzten HB
+  hbs = remaining_heart_beats + QuerySkillAttribute(SA_SPEED);
+  if ( hbs <= 0 )
+    hbs = 100; 
+
+  // P_ATTACK_BUSY bestimmen
+  if ( attack_busy > 99) 
+   attack_busy = 100 + hbs;
+  else
+    attack_busy += 100 + hbs;
+  // mehr fuer Seher...
+  if ( IS_SEER(ME) )
+    attack_busy+=(100+QueryProp(P_LEVEL)); 
+  // max. 500, d.h. 5 Attacken
+  if ( attack_busy>500 )
+    attack_busy=500;
+
+  // unganzzahligen Rest fuer naechsten HB speichern
+  remaining_heart_beats = hbs % 100;
+  hbs /= 100; // ganze Attacken. ;-)
+
+  if ( hbs > 10 ) // nicht mehr als 10 Attacken.
+    hbs = 10; 
+
+  // Falls jemand seit dem letzten HB in diesem Living von aussen Attack()
+  // gerufen hat (nicht ExtraAttack()), wird jetzt was abgezogen.
+  hbs -= no_more_attacks;
+  no_more_attacks = 0;
+
+  // Wie lange verfolgt dieser NPC? Wenn Prop nicht gesetzt oder ungueltig,
+  // d.h. kein int oder <= 0, dann wird die Defaultzeit genommen.
+  int hunttime = (int)QueryProp(P_HUNTTIME);
+  hunttime = (intp(hunttime) && hunttime > 0) ? 
+              hunttime / __HEART_BEAT_INTERVAL__ : HUNTTIME;
+  // erstmal die Hunttimes der Gegner aktualisieren und nebenbei die
+  // anwesenden Feinde merken. Ausserdem ggf. Kampf abbrechen, wenn
+  // P_NO_ATTACK gesetzt ist.
+  // eigentlich ist diese ganze Schleife fast das gleiche wie
+  // PresentEnemies(), aber leider muessen ja die Hunttimes aktulisiert werde,
+  // daher kann man nicht direkt PresentEnemies() nehmen.
+  // TODO: Diese Schleife und PresentEnmies() irgendwie vereinigen.
+  object *netdead=({});
+  object *here=({});
+  object enemy;
+  mixed no_attack;
+  foreach(enemy, int htime: &enemies) { // Keys in enemy
+    // ggf. Meldungen ausgeben und Gegner austragen und dieses Living beim
+    // Gegner austragen.
+    if (no_attack=enemy->QueryProp(P_NO_ATTACK)) {
+      m_delete(enemies,enemy);
+        if ( stringp(no_attack) ) {
+            write( no_attack );
+            enemy->StopHuntFor( ME, 1 );
+        }
+        else
+            enemy->StopHuntFor( ME, 0 );
+    }
+    else if ( environment()==environment(enemy) ) {
+      // Gegner anwesend, als Feind merken...
+      if ( interactive(enemy) || !query_once_interactive(enemy) )
+        here+=({enemy});
+      else
+        netdead+=({enemy});
+      // ... und Hunttime erneuern.
+      htime = hunttime;
+    }
+    // Feind nicht anwesend. Timer dekrementieren und Feind austragen, wenn
+    // Jagdzeit abgelaufen.
+    else if ( (--htime) <= 0 )
+      StopHuntFor(enemy);
+  }
+  // Netztote sind nur Feinde, wenn sonst keine da sind.
+  if ( !sizeof(here) )
+    here=netdead;
+
+  // schonmal abfragen, ob ein Spell vorbereitet wird.
+  mixed pspell=QueryProp(P_PREPARED_SPELL);
+
+  //Da hbs 0 und no_more_attacks durch einen manuellen Aufruf von Attack() im
+  //Spieler >0 sein kann, kann jetzt hbs <0 sein. (s.o.)
+  if (hbs > 0 && sizeof(here)) {
+    foreach(int i: hbs) {
+      // Feind in Nahkampfreichweite finden
+      if ( objectp(enemy=SelectNearEnemy(here)) ) {
+        // Flucht erwuenscht?
+        if ( QueryProp(P_WIMPY) > QueryProp(P_HP) ) {
+          Flee();
+          // Flucht gelungen?
+          if ( enemy && (environment(enemy)!=environment()) ) {
+            here = 0; // naechste Runde neue Feinde suchen
+            enemy = 0;
+            continue; // kein Kampf, Runde ueberspringen
+          }
+        }
+      }
+      else {
+        // keine Feinde gefunden... Abbrechen.
+        break;
+      }
+      // Paralyse gesetzt? -> Abbrechen. Dieses Pruefung muss hier passieren,
+      // damit ggf. die automatische Flucht des Lebewesens durchgefuehrt wird,
+      // auch wenn es paralysiert ist.
+      if (dis_attack > 0) break;
+
+      // Kampf durch Spell-Prepare blockiert?
+      // Keine genaue Abfrage, wird spaeter noch genau geprueft.
+      if ( pspell && QueryProp(P_GUILD_PREPAREBLOCK) )
+        break; // keine Angriffe in diesem HB.
+      // wenn Feind da: hit'em hard.
+      if ( objectp(enemy) )
+        Attack(enemy);
+      // ggf. hat Attack() no_more_attacks gesetzt. Zuruecksetzen
+      no_more_attacks = 0;
+      // naechste Kampfrunde neue Feinde suchen
+      here = 0;
+    }  // foreach
+  }
+
+  no_more_attacks=0;
+
+  // Ist ein Spell in Vorbereitung und evtl. jetzt fertig vorbereitet?
+  if ( pointerp(pspell)
+      && (sizeof(pspell)>=3) && intp(pspell[0]) && stringp(pspell[1]) ) {
+    if ( time()>=pspell[0] ) // Kann der Spruch jetzt ausgefuehrt werden?
+    {
+      UseSpell(pspell[2],pspell[1]); // Dann los
+      SetProp(P_PREPARED_SPELL,0);
+    }
+  }
+  else if ( pspell ) // Unbrauchbarer Wert, loeschen
+    SetProp(P_PREPARED_SPELL,0);
+
+} // Ende heart_beat
+
+// wird von NPC gerufen, die Heartbeats nachholen und dabei die Hunttimes um
+// die Anzahl verpasster Heartbeats reduzieren muessen.
+// Wird von Spielerobjekten gerufen, wenn sie nach 'schlafe ein' aufwachen, um
+// die notwendige Anzahl an HB hier abzuziehen und ggf. die Feinde zu expiren,
+// da Spieler ja netztot keinen Heartbeat haben.
+protected void update_hunt_times(int beats) {
+  if (!mappingp(enemies)) return;
+  foreach(object en, int htime: &enemies) { // Mapping-Keys in en
+    htime -= beats;
+    if ( htime <= 0 )
+      StopHuntFor(en);
+  }
+}
+
+public int IsEnemy(object wer)
+{
+  return (member(enemies,wer));
+}
+
+public void StopHuntText(object arg)
+{
+  tell_object(arg,
+    Name(WER,1)+" "+(QueryProp(P_PLURAL)?"jagen ":"jagt ")+
+               (arg->QueryProp(P_PLURAL)?"Euch":"Dich")+" nicht mehr.\n");
+  tell_object(ME,(QueryProp(P_PLURAL)?"Ihr jagt ":"Du jagst ")+
+                 arg->name(WEN,1)+" nicht mehr.\n");
+}
+
+public varargs int StopHuntFor(object arg, int silent)
+{
+  if ( !objectp(arg) || !IsEnemy(arg) )
+    return 0;
+
+  if (!silent)
+    StopHuntText(arg);
+
+  m_delete(enemies,arg);
+  last_attack_msg=0;
+
+  return 1;
+}
+
+// Begruessungsschlag nur einmal pro HB
+public void Attack2(object enemy)
+{
+  if ( att2_time > time() )
+    return;
+
+  att2_time=time() + __HEART_BEAT_INTERVAL__;
+  Attack(enemy);
+}
+
+// Fuer evtl. Attack-Aenderungen, ohne dass man gleich das ganze
+// Attack ueberlagern muss:
+protected void InternalModifyAttack(mapping ainfo)
+{
+  return; // Vorerst!
+/*
+  int   fac;
+  mixed res;
+
+  fac=100;
+
+  if (CannotSee(1))
+    fac -= 50;
+
+  // Weitere Mali?
+
+  if (fac<100)
+    ainfo[SI_SKILLDAMAGE] = ainfo[SI_SKILLDAMAGE]*fac/100;
+  // Fertig
+*/
+}
+
+// Ausfuehren Eines Angriffs auch wenn es bereits einen Angriff
+// in dieser Runde gegeben hat. Dieser Angriff wird auch nicht
+// gezaehlt. Die Nutzung dieser Funktion ist nur fuer Spezialfaelle
+// gedacht und immer BALANCEPFLICHTIG!
+varargs public void ExtraAttack(object enemy, int ignore_previous)
+{
+  int saved_no_more_attacks;
+
+  // Erstschlagsinfo speichern.
+  saved_no_more_attacks = no_more_attacks;
+
+  // Bei Bedarf bisher schon durchgefuehrte Erstschlaege ignorieren
+  if (ignore_previous) no_more_attacks=0;
+
+  // Normalen Angriff durchfuehren
+  Attack (enemy);
+
+  // Gespeicherten Wert zuruecksetzen
+  no_more_attacks = saved_no_more_attacks;
+
+  return;
+}
+
+public void Attack(object enemy)
+{
+  closure cl;
+  mixed   res;
+  mapping ainfo;
+  mapping edefendinfo; // erweiterte Defend-Infos
+  mixed hookData;
+  mixed hookRes;
+
+  if ( no_more_attacks || QueryProp(P_GHOST) || 
+      !objectp(enemy) || !objectp(this_object()) || 
+      (QueryProp(P_DISABLE_ATTACK) > 0) || enemy->QueryProp(P_NO_ATTACK) || 
+      (query_once_interactive(this_object()) && !interactive(this_object())) )
+    return;
+
+  edefendinfo=([]);
+
+  Set(P_LAST_COMBAT_TIME,time());
+
+  // inkrementieren. Diese Variable wird im HB nach den Angriffen genullt.
+  // Diese Variable wird im naechsten Heartbeat von der Anzahl an verfuegbaren
+  // Angriffen des Livings abgezogen. Dies beruecksichtigt also zwischen den
+  // HBs (z.B. extern) gerufene Attacks().
+  no_more_attacks++;
+
+  // Wird das Attack durch einen temporaeren Attack-Hook ersetzt?
+  if ( res=QueryProp(P_TMP_ATTACK_HOOK) )
+  {
+    if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0])
+        && objectp(res[1]) && stringp(res[2]) )
+    {
+      if ( !(res=call_other(res[1],res[2],enemy)) )
+        return;
+    }
+    else
+      SetProp(P_TMP_ATTACK_HOOK,0);
+  }
+
+  // trigger attack hook
+  hookData=({enemy});
+  hookRes=HookFlow(H_HOOK_ATTACK,hookData);
+  if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+    if(hookRes[H_RETCODE]==H_CANCELLED){
+      return;
+    }
+  }
+
+  ainfo = ([ SI_ENEMY : enemy,
+             SI_SPELL : 0,
+           ]);
+
+  if ( objectp(ainfo[P_WEAPON]=QueryProp(P_WEAPON)) )
+  {
+    ainfo[P_WEAPON]->TakeFlaw(enemy);
+
+    // Abfrage fuer den Fall, dass Waffe durch das TakeFlaw() zerstoert
+    // wurde. Dann wird das Attack abgebrochen.
+    if ( !objectp(ainfo[P_WEAPON]) )
+      return;
+
+    cl=symbol_function("name",ainfo[P_WEAPON]);
+    ainfo[SI_SKILLDAMAGE_MSG] = (" mit "+(string)funcall(cl,WEM,0));
+    ainfo[SI_SKILLDAMAGE_MSG2] = (" mit "+(string)funcall(cl,WEM,1));
+
+    ainfo[SI_SKILLDAMAGE] = (int)ainfo[P_WEAPON]->QueryDamage(enemy);
+
+    cl=symbol_function("QueryProp",ainfo[P_WEAPON]);
+    ainfo[SI_SKILLDAMAGE_TYPE] = funcall(cl,P_DAM_TYPE);
+    ainfo[P_WEAPON_TYPE] = (string)funcall(cl,P_WEAPON_TYPE);
+    ainfo[P_NR_HANDS] = (int)funcall(cl,P_NR_HANDS);
+    ainfo[P_WC] = (int)funcall(cl,P_WC);
+
+    // Zweihaendige Waffe?
+    if ( ainfo[P_NR_HANDS]==2
+        && mappingp(res=UseSkill(SK_TWOHANDED,deep_copy(ainfo)))
+        && member(res,SI_SKILLDAMAGE) )
+    {
+      // Nur den neuen Schadenswert uebernehmen.
+      ainfo[SI_SKILLDAMAGE]=((int)(res[SI_SKILLDAMAGE]));
+    }
+  }
+  else // Keine Waffe gezueckt
+  {
+    /* Check if there is a magical attack */
+    if ( mappingp(res=UseSkill(SK_MAGIC_ATTACK,([SI_ENEMY:enemy]))) )
+    {
+      ainfo[SI_SKILLDAMAGE]=(int)res[SI_SKILLDAMAGE];
+      ainfo[SI_SKILLDAMAGE_TYPE]=res[SI_SKILLDAMAGE_TYPE];
+
+      if ( stringp(res[SI_SKILLDAMAGE_MSG]) )
+        ainfo[SI_SKILLDAMAGE_MSG] = " mit "+(string)res[SI_SKILLDAMAGE_MSG];
+      else
+        ainfo[SI_SKILLDAMAGE_MSG] = " mit magischen Faehigkeiten";
+
+      if ( stringp(res[SI_SKILLDAMAGE_MSG2]) )
+        ainfo[SI_SKILLDAMAGE_MSG2] = " mit "+(string)res[SI_SKILLDAMAGE_MSG2];
+      else
+        ainfo[SI_SKILLDAMAGE_MSG2] = (string)ainfo[SI_SKILLDAMAGE_MSG];
+
+      if ( !(ainfo[P_WEAPON_TYPE]=res[P_WEAPON_TYPE]) )
+        ainfo[P_WEAPON_TYPE]=WT_MAGIC;
+
+      if ( member(res,SI_SPELL) )
+        ainfo[SI_SPELL]=res[SI_SPELL];
+    }
+    else
+    {
+      /* Ohne (freie) Haende wird auch nicht angegriffen */
+      if ( interactive(this_object())
+          && (QueryProp(P_USED_HANDS) >= QueryProp(P_MAX_HANDS)) )
+        return ;
+
+      if ( !pointerp(res=QueryProp(P_HANDS)) || (sizeof(res)<2) )
+        return;
+
+      ainfo[SI_SKILLDAMAGE] = (( 2*random(((int)res[1])+1)
+                              + 10*(QueryAttribute(A_STR)) )/3);
+      ainfo[SI_SKILLDAMAGE_TYPE] = res[2];
+      ainfo[SI_SKILLDAMAGE_MSG] = ainfo[SI_SKILLDAMAGE_MSG2]
+                                = ((string)res[0]);
+      ainfo[P_WEAPON_TYPE] = WT_HANDS;
+      ainfo[P_WC] = (int)res[1];
+    }
+  }  // besondere Faehigkeiten mit diesem Waffentyp?
+  if ( mappingp(res=UseSkill(FIGHT(ainfo[P_WEAPON_TYPE]),
+                             deep_copy(ainfo))) )
+    SkillResTransfer(res,ainfo);
+
+  // besondere allgemeine Kampffaehigkeiten?
+  if ( mappingp(res=UseSkill(SK_FIGHT,deep_copy(ainfo))) )
+    SkillResTransfer(res,ainfo);
+
+  // Veraenderungen durch einen Attack-Modifier?
+  if ( (res=QueryProp(P_TMP_ATTACK_MOD)) )
+  {
+    if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0])
+        && (time()<res[0]) && objectp(res[1]) && stringp(res[2]) )
+    {
+      if ( !(res=call_other(res[1],res[2],deep_copy(ainfo)))
+          || !mappingp(res) )
+        return;
+      else
+        SkillResTransfer(res,ainfo);
+    }
+    else
+      SetProp(P_TMP_ATTACK_MOD,0);
+  }
+
+  // trigger attack mod hook
+  hookData=deep_copy(ainfo);
+  hookRes=HookFlow(H_HOOK_ATTACK_MOD,hookData);
+  if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+    if(hookRes[H_RETCODE]==H_CANCELLED){
+      return;
+    }
+    else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] &&
+             mappingp(hookRes[H_RETDATA])){
+        SkillResTransfer(hookRes[H_RETDATA],ainfo);
+    }
+  }
+
+  // Interne Modifikationen der Angriffswerte
+  InternalModifyAttack(ainfo);
+
+  if ( !objectp(enemy) )
+    return;
+
+  // hier mal bewusst nicht auf P_PLURAL zuerst testen. in 90% der Faelle
+  // wird eh keine Angriffsmeldung mehr ausgegeben, also pruefen wir das
+  // lieber zuerst. Plural testen zieht schon genug Rechenzeit :/
+  // Nicht meine Idee! Nicht meine Idee! Nachtwind 7.7.2001
+  if ( ainfo[SI_SKILLDAMAGE_MSG2]!=last_attack_msg )
+  {
+    last_attack_msg = ainfo[SI_SKILLDAMAGE_MSG2];
+    if (QueryProp(P_PLURAL))
+    {
+      tell_object( ME, "  Ihr greift " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+      say("  "+(Name(WER,1))+" greifen "+(enemy->name(WEN,1))+
+          ainfo[SI_SKILLDAMAGE_MSG]+" an.\n", enemy );
+      tell_object( enemy, "  "+(Name(WER,1))+" greifen "+
+                  (enemy->QueryProp(P_PLURAL)?"Euch":"Dich")+
+                   ainfo[SI_SKILLDAMAGE_MSG2]+" an.\n" );
+    }
+    else
+    {
+      tell_object( ME, "  Du greifst " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+      say("  "+(Name(WER,2))+" greift "+(enemy->name(WEN,1))+
+          ainfo[SI_SKILLDAMAGE_MSG]+" an.\n", enemy );
+      tell_object( enemy, "  "+(Name(WER,1))+" greift "+
+                  (enemy->QueryProp(P_PLURAL)?"Euch":"Dich")+
+                   ainfo[SI_SKILLDAMAGE_MSG2]+" an.\n" );    }
+  }
+  else if ( Query(P_SHOW_ATTACK_MSG) )
+    if (QueryProp(P_PLURAL))
+      tell_object( ME, "  Ihr greift " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+    else
+      tell_object( ME, "  Du greifst " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+
+  // ainfo in defendinfo merken
+  edefendinfo[ ORIGINAL_AINFO]= deep_copy(ainfo);
+  edefendinfo[ ORIGINAL_DAM]= ainfo[SI_SKILLDAMAGE];
+  edefendinfo[ ORIGINAL_DAMTYPE]= ainfo[SI_SKILLDAMAGE_TYPE];
+  edefendinfo[ CURRENT_DAM]= ainfo[SI_SKILLDAMAGE];
+  edefendinfo[ CURRENT_DAMTYPE]= ainfo[SI_SKILLDAMAGE_TYPE];
+
+  // ainfo[SI_SPELL] auf ein mapping normieren
+  if ( intp(ainfo[SI_SPELL]) )
+  {
+    ainfo[SI_SPELL] = ([ SP_PHYSICAL_ATTACK : !ainfo[SI_SPELL],
+               SP_SHOW_DAMAGE     : !ainfo[SI_SPELL],
+               SP_REDUCE_ARMOUR   : ([ ])  ]);
+  }
+
+  // defendinfo anhaengen, falls ainfo[SI_SPELL] ein mapping ist
+  if( mappingp(ainfo[SI_SPELL]))
+  {
+    ainfo[SI_SPELL][EINFO_DEFEND]=edefendinfo;
+  }
+
+
+  enemy->Defend(ainfo[SI_SKILLDAMAGE], ainfo[SI_SKILLDAMAGE_TYPE],
+                ainfo[SI_SPELL],       this_object());
+
+
+  //edefendinfo=([]);
+
+  /* Done attacking */
+}
+
+public void AddDefender(object friend)
+{ object *defs;
+
+  if ( !objectp(friend) )
+    return;
+
+  if ( !pointerp(defs=QueryProp(P_DEFENDERS)) )
+    defs=({});
+
+  if ( member(defs,friend)>=0 )
+    return;
+
+  defs+=({friend});
+  SetProp(P_DEFENDERS,defs);
+}
+
+public void RemoveDefender(object friend)
+{ object *defs;
+
+  if ( !objectp(friend) )
+    return;
+
+  if ( !pointerp(defs=QueryProp(P_DEFENDERS)) )
+    defs=({});
+
+  if ( member(defs,friend)==-1 )
+    return;
+
+  defs -= ({friend});
+  SetProp(P_DEFENDERS,defs);
+}
+
+public void InformDefend(object enemy)
+{
+  UseSkill(SK_INFORM_DEFEND,([ SI_ENEMY  : enemy,
+                               SI_FRIEND : previous_object() ]));
+  // Oh, oh - ich hoffe mal, dass InformDefend wirklich NUR aus Defend
+  // eines befreundeten livings aufgerufen wird... (Silvana)
+  // This is only experimental... ;)
+}
+
+public <int|string*|mapping>* DefendOther(int dam, string|string* dam_type,
+                                          int|mapping spell, object enemy)
+{
+  <int|string*|mapping>* res;
+
+  if ( (res=UseSkill(SK_DEFEND_OTHER,([ SI_SKILLDAMAGE : dam,
+                                        SI_SKILLDAMAGE_TYPE : dam_type,
+                                        SI_SPELL : spell,
+                                        SI_FRIEND : previous_object(),
+                                        SI_ENEMY : enemy ])))
+      && pointerp(res) )
+    return res;
+
+  return 0;
+}
+
+public void CheckWimpyAndFlee()
+{
+  if ( (QueryProp(P_WIMPY)>QueryProp(P_HP)) && !TeamFlee()
+      && find_call_out("Flee")<0 )
+    call_out(#'Flee,0,environment());
+}
+
+protected string mess(string msg,object me,object enemy)
+{ closure mname, ename;
+  string  *parts,x;
+  int     i;
+
+  mname = symbol_function("name", me);
+  ename = symbol_function("name", enemy);
+
+  parts=regexplode(msg,"@WE[A-Z]*[12]");
+  for ( i=sizeof(parts)-2 ; i>=1 ; i-=2 )
+  {
+    switch(parts[i])
+    {
+      case "@WER1":    parts[i]=funcall(mname,WER,1);    break;
+      case "@WESSEN1": parts[i]=funcall(mname,WESSEN,1); break;
+      case "@WEM1":    parts[i]=funcall(mname,WEM,1);    break;
+      case "@WEN1":    parts[i]=funcall(mname,WEN,1);    break;
+      case "@WER2":    parts[i]=funcall(ename,WER,1);    break;
+      case "@WESSEN2": parts[i]=funcall(ename,WESSEN,1); break;
+      case "@WEM2":    parts[i]=funcall(ename,WEM,1);    break;
+      case "@WEN2":    parts[i]=funcall(ename,WEN,1);    break;
+      default: ;
+    }
+  }
+
+  return break_string(capitalize(implode(parts,"")),78,"  ",1);
+}
+
+// Fuer evtl. Defend-Aenderungen, ohne dass man gleich das ganze
+// Attack ueberlagern muss:
+protected void InternalModifyDefend(int dam, string* dt, mapping spell, object enemy)
+{
+  return;
+}
+
+public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy)
+{
+  int     i,k;
+  mixed   res,res2;
+  object  *armours,tmp;
+  mixed hookData;
+  mixed hookRes;
+  
+  //  string  what, how;
+  string enname, myname;
+
+  // this_player(), wenn kein enemy bekannt...
+  enemy ||= this_player();
+  // Testen, ob dieses Lebewesen ueberhaupt angegriffen werden darf
+  if ( !this_object() || !enemy || QueryProp(P_NO_ATTACK)
+      || ( query_once_interactive(enemy) && ! interactive(enemy) ) )
+    return 0;
+
+  if ( intp(spell) )
+    spell = ([ SP_PHYSICAL_ATTACK : !spell,
+               SP_SHOW_DAMAGE     : !spell,
+               SP_REDUCE_ARMOUR   : ([ ])  ]);
+  else if ( !mappingp(spell) ) // Illegaler spell-Parameter
+    return 0;
+
+  // testen ob eine erweiterte defendinfo vorhanden ist
+  if(!member(spell,EINFO_DEFEND))
+  {
+          //spell+=([EINFO_DEFEND:([])]); // ggf hinzufuegen
+        // use a temporary mapping to avoid recursive
+        // val[x][y] = deep_copy(val);
+        mapping tmpdefend = ([
+                ORIGINAL_AINFO:deep_copy(spell),
+                ORIGINAL_DAM:dam,
+                  ORIGINAL_DAMTYPE:dam_type,
+        ]);
+          spell[EINFO_DEFEND]=tmpdefend;
+  }
+
+  // Schadenstyp ueberpruefen
+  if ( !pointerp(dam_type) )
+    dam_type = ({ dam_type });
+
+  spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Testen, ob der Angreifer schon als Feind registriert worden ist.
+  // Wenn nein, registrieren.
+  if ( !IsEnemy(enemy) && !spell[SP_NO_ENEMY] )
+  {
+    spell[EINFO_DEFEND][ENEMY_INSERTED]=1;
+    InsertEnemy(enemy);
+  }
+
+  // RFR-Taktik abfangen
+  if ( !QueryProp(P_ENABLE_IN_ATTACK_OUT) )
+  {
+    i=time()-(enemy->QueryProp(P_LAST_MOVE));
+    // Gegner hat sich bewegt, man selbst nicht
+    if ( (i<3) && (time()-QueryProp(P_LAST_MOVE)>=5) )
+    {
+      // Bei Erster Kampfrunde wenige Schaden
+      dam/=(4-i);
+      spell[EINFO_DEFEND][RFR_REDUCE]=dam;
+      spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+    }
+  }
+
+  // Man kann Verteidiger haben. Diese kommen als erste zum Zuge
+  if ( res=QueryProp(P_DEFENDERS) )
+  { object *defs,*defs_here;
+
+    defs=({});
+    defs_here=({});
+    if ( !pointerp(res) )
+      res=({res});
+    // erst alle anwesenden finden.
+    foreach(object defender: res) {
+      if ( objectp(defender) && (member(defs,defender)<0) )
+      {
+        defs+=({defender});
+        //  Verteidiger muessen im gleichen Raum oder im Living selber
+        //  enthalten sein.
+        if ( environment(defender) == environment()
+            || environment(defender) == ME)
+        {
+          call_other(defender,"InformDefend",enemy);
+          if (defender)
+            defs_here += ({ defender });
+        }
+      }
+    }
+    //Anwesende Verteidiger eintragen.
+    spell[EINFO_DEFEND][PRESENT_DEFENDERS]=defs_here;
+
+    // P_DEFENDERS auch gleich aktualisieren
+    if ( sizeof(defs)<1 )
+      defs=0;
+    SetProp(P_DEFENDERS,defs);
+
+    if ( spell[SP_PHYSICAL_ATTACK] ) {
+      // Bei physischen Angriffen nur Verteidiger aus Reihe 1
+      // nehmen (z.B. fuer Rueckendeckung)
+      foreach(object defender: defs_here) {
+        if ( (defender->PresentPosition())>1 ) {
+          defs_here-=({defender});
+        }
+      }
+    }
+ 
+    if ( (i=sizeof(defs_here)) )
+    {
+      mixed edefendtmp=({defs_here[random(i)],0,0,0}); 
+      res=call_other(edefendtmp[DEF_DEFENDER],"DefendOther",
+                     dam,dam_type,spell,enemy);
+      if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0])
+          && pointerp(res[1]))
+      {
+        // Helfer koennen den Schaden oder Schadenstyp aendern,
+        // z.B. Umwandlung von Feuer nach Eis oder so...
+        dam=res[0];
+        edefendtmp[DEF_DAM]=dam;
+        dam_type=res[1];
+        edefendtmp[DEF_DAMTYPE]=dam_type;
+
+        if ( mappingp(res[2]) )
+        {
+          spell=res[2];
+          // teuer, aber geht nicht anders (Rekursion vermeiden)
+          edefendtmp[DEF_SPELL]=deep_copy(res[2]);
+        }
+        spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+        spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+      }
+      spell[EINFO_DEFEND][DEFENDING_DEFENDER]=edefendtmp;
+    }
+  } // Ende Defender-Verarbeitung
+
+
+  // Ueber einen P_TMP_DEFEND_HOOK werden z.B. Schutzzauber gehandhabt
+  spell[EINFO_DEFEND][DEFEND_HOOK]=DI_NOHOOK;
+  if ( res=QueryProp(P_TMP_DEFEND_HOOK) )
+  {
+    if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0])
+        && objectp(res[1]) && stringp(res[2]) )
+    {
+      if ( !(res=call_other(res[1],res[2],dam,dam_type,spell,enemy)) )
+      {
+        // Ein P_TMP_DEFEND_HOOK kann den Schaden vollstaendig abfangen,
+        // das Defend wird dann hier abgebrochen *SICK* und es wird nur
+        // noch getestet, ob man in die Flucht geschlagen wurde
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT;
+        CheckWimpyAndFlee();
+        return 0;
+      }
+      else
+      {
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK;
+        if ( pointerp(res) && (sizeof(res)>=3)
+            && intp(res[0] && pointerp(res[1])) )
+        {
+          mixed edefendtmp=({0,0,0});
+          // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und
+          // -art sowie die Spell-Infos veraendern
+          dam=res[0];
+          edefendtmp[HOOK_DAM]=dam;
+          dam_type=res[1];
+          edefendtmp[HOOK_DAMTYPE]=dam_type;
+
+          if ( mappingp(res[2]) )
+          {
+            spell=res[2];
+            // Waeh. Teuer. Aber geht nicht anders.
+            edefendtmp[HOOK_SPELL]=deep_copy(spell);
+          }
+          spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp;
+          spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+          spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+        }
+      }
+    }
+    else
+      SetProp(P_TMP_DEFEND_HOOK,0);
+  } // P_TMP_DEFEND_HOOK
+
+  // trigger defend hook
+  hookData=({dam,dam_type,spell,enemy});
+  hookRes=HookFlow(H_HOOK_DEFEND,hookData);
+  if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+    if(hookRes[H_RETCODE]==H_CANCELLED){
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT;
+        CheckWimpyAndFlee();
+        return 0;
+    }
+    else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA]){
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK;
+        if ( pointerp(hookRes[H_RETDATA]) && (sizeof(hookRes[H_RETDATA])>=3)
+            && intp(hookRes[H_RETDATA][0] && pointerp(hookRes[H_RETDATA][1])) )
+        {
+          mixed edefendtmp=({0,0,0});
+          // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und
+          // -art sowie die Spell-Infos veraendern
+          dam=hookRes[H_RETDATA][0];
+          edefendtmp[HOOK_DAM]=dam;
+          dam_type=hookRes[H_RETDATA][1];
+          edefendtmp[HOOK_DAMTYPE]=dam_type;
+
+          if ( mappingp(hookRes[H_RETDATA][2]) )
+          {
+            spell=hookRes[H_RETDATA][2];
+            // Teuer, teuer... :-(
+            edefendtmp[HOOK_SPELL]=deep_copy(spell);
+          }
+          spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp;
+          spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+          spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+        }
+    }
+  } // Ende Hook-Behandlung
+
+  if ( !member(spell,SP_REDUCE_ARMOUR) || !mappingp(spell[SP_REDUCE_ARMOUR]) )
+        spell[SP_REDUCE_ARMOUR] = ([]);
+
+  // Es gibt auch Parierwaffen,
+  if ( objectp(tmp=QueryProp(P_PARRY_WEAPON)) )
+  {
+    res2=tmp->QueryDefend(dam_type, spell, enemy);
+
+    // Reduzierbare Wirksamkeit der Parierwaffe?
+    if ( member(spell[SP_REDUCE_ARMOUR],P_PARRY_WEAPON)
+        && intp(res=spell[SP_REDUCE_ARMOUR][P_PARRY_WEAPON]) && (res>=0) )
+    {
+      res2=(res2*res)/100;
+    }
+
+    dam-=res2;
+    spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+  }
+
+  // Jetzt kommen die Ruestungen des Lebewesens ins Spiel (wenn es denn
+  // welche traegt)
+
+  armours=QueryProp(P_ARMOURS)-({0});
+  if ( (i=sizeof(armours))>0 ) { 
+    string aty;
+
+    tmp=armours[random(i)];
+
+    if ( objectp(tmp) )
+      //Uebergabe des Mappings eh als Pointer/Referenz, daher billig
+      tmp->TakeFlaw(dam_type,spell[EINFO_DEFEND]);
+
+    // pro Ruestung ein Key an Platz reservieren
+    spell[EINFO_DEFEND][DEFEND_ARMOURS]=m_allocate(i,2);
+    foreach(object armour : armours) {
+      if ( objectp(armour) ) {
+        aty=(string)armour->QueryProp(P_ARMOUR_TYPE);
+
+        if ( member(spell[SP_REDUCE_ARMOUR],aty)
+            && intp(res2=spell[SP_REDUCE_ARMOUR][aty]) && (res2>=0) )
+          dam -= (res2*armour->QueryDefend(dam_type, spell, enemy))/100;
+        else
+          dam -= (int)(armour->QueryDefend(dam_type, spell, enemy));
+        // Schaden NACH DefendFunc vermerken. 
+        // Schutzwirkung VOR DefendFunc (DEF_ARMOUR_PROT) vermerkt
+        // das QueryDefend() selber.
+        spell[EINFO_DEFEND][DEFEND_ARMOURS][armour,DEF_ARMOUR_DAM]=dam;
+        // akt. Schaden vermerken und Schutz der aktuellen Ruestung (vor
+        // DefendFunc) wieder auf 0 setzen fuer den naechsten Durchlauf.
+        spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+        spell[EINFO_DEFEND][DEFEND_CUR_ARMOUR_PROT]=0;
+      }
+    }
+  }
+
+  // Manche Gilden haben einen Verteidigungsskill. Der kommt jetzt zum
+  // tragen. Der Skill kann die Schadenshoehe und -art beeinflussen.
+  spell[EINFO_DEFEND]+=([DEFEND_GUILD:({})]);
+  if ( mappingp(res=UseSkill(SK_MAGIC_DEFENSE,
+                            ([ SI_ENEMY            : enemy,
+                               SI_SKILLDAMAGE      : dam,
+                               SI_SKILLDAMAGE_TYPE : dam_type,
+                               SI_SPELL            : spell     ]))) )
+  {
+    dam=(int)res[SI_SKILLDAMAGE];
+
+    if ( pointerp(res[SI_SKILLDAMAGE_TYPE]) )
+        dam_type=res[SI_SKILLDAMAGE_TYPE];
+
+    spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+    spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+    spell[EINFO_DEFEND][DEFEND_GUILD]=({dam,dam_type});
+  }
+
+  // Evtl. interne Modifikationen
+  InternalModifyDefend(&dam, &dam_type, &spell, &enemy);
+
+  spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Testen, ob irgendwas im Inventory des Lebewesen auf den Angriff
+  // "reagiert"
+  CheckSensitiveAttack(dam,dam_type,spell,enemy);
+
+  if ( !objectp(enemy) )
+    return 0;
+
+  // Angriffszeit im Gegner setzen
+  enemy->SetProp(P_LAST_COMBAT_TIME,time());
+
+  // Die Resistenzen des Lebewesens (natuerliche und durch Ruestungen
+  // gesetzte) beruecksichtigen
+  dam = to_int(CheckResistance(dam_type)*dam);
+  spell[EINFO_DEFEND][DEFEND_RESI]=dam;
+
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Bei physikalischen Angriffen wird die natuerliche Panzerung und die
+  // Geschicklichkeit des Lebewesens beruecksichtigt
+  object stat = find_object("/d/erzmagier/zesstra/pacstat"); // TODO: remove
+  if ( spell[SP_PHYSICAL_ATTACK] )
+  {
+    // Minimum ist auch hier 1.
+    int body = QueryProp(P_BODY)+QueryAttribute(A_DEX);
+    res2 = (body/4 + random(body*3/4 + 1)) || 1;
+    if (stat)
+      stat->bodystat(body, res2, random(body)+1);
+
+    // Reduzierbare Wirksamkeit des Bodies?
+    if ( member(spell[SP_REDUCE_ARMOUR], P_BODY)
+        && intp(res=spell[SP_REDUCE_ARMOUR][P_BODY]) && (res>=0) )
+      res2=(res2*res)/100;
+
+    dam-=res2;
+  }
+  spell[EINFO_DEFEND][DEFEND_BODY]=dam;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Ist ueberhaupt noch etwas vom Schaden uebrig geblieben?
+  if ( dam<0 )
+    dam = 0;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // fuer die Statistik
+  // TODO: entfernen nach Test-Uptime
+  if (stat)
+    stat->damagestat(spell[EINFO_DEFEND]);
+
+  // Die Anzahl der abzuziehenden Lebenspunkte ist der durch 10 geteilte
+  // Schadenswert
+  dam = dam / 10;
+  spell[EINFO_DEFEND][DEFEND_LOSTLP]=dam;
+
+  // evtl. hat entweder der Aufrufer oder das Lebewesen selber eigene
+  // Schadensmeldungen definiert. Die vom Aufrufer haben Prioritaet.
+  mixed dam_msg = spell[SP_SHOW_DAMAGE];
+  // Wenn != 0 (d.h. Treffermeldungen grundsaetzlich erwuenscht), aber kein
+  // Array, hier im Living fragen.
+  if (dam_msg && !pointerp(dam_msg))
+    dam_msg = QueryProp(P_DAMAGE_MSG);
+
+  // In den meisten Faellen soll auch eine Schadensmeldung ausgegeben
+  // werden, die die Hoehe des Schadens (grob) anzeigt
+  if (spell[SP_SHOW_DAMAGE] && !pointerp(dam_msg)) {
+    myname=name(WEN);
+    enname=enemy->Name(WER);
+    if (enemy->QueryProp(P_PLURAL)) {
+      switch (dam) {
+      case 0:
+        tell_object(enemy,sprintf("  Ihr verfehlt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s verfehlen Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+           sprintf("  %s verfehlen %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 1:
+        tell_object(enemy,sprintf("  Ihr kitzelt %s am Bauch.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s kitzeln Dich am Bauch.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kitzeln %s am Bauch.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 2..3:
+        tell_object(enemy,sprintf("  Ihr kratzt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s kratzen Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kratzen %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 4..5:
+        tell_object(enemy,sprintf("  Ihr trefft %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s treffen Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s treffen %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 6..10:
+        tell_object(enemy,sprintf("  Ihr trefft %s hart.\n",myname));
+        tell_object(this_object(),sprintf("  %s treffen Dich hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s treffen %s hart.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 11..20:
+        tell_object(enemy,sprintf("  Ihr trefft %s sehr hart.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s treffen Dich sehr hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s treffen %s sehr hart.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 21..30:
+        tell_object(enemy,
+          sprintf("  Ihr schlagt %s mit dem Krachen brechender Knochen.\n",
+                  myname));
+        tell_object(this_object(),
+          sprintf("  %s schlagen Dich mit dem Krachen brechender Knochen.\n",
+                  enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlagen %s mit dem Krachen brechender Knochen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 31..50:
+        tell_object(enemy,
+          sprintf("  Ihr zerschmettert %s in kleine Stueckchen.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s zerschmettern Dich in kleine Stueckchen.\n",
+                                          enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerschmettern %s in kleine Stueckchen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 51..75:
+        tell_object(enemy,sprintf("  Ihr schlagt %s zu Brei.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s schlagen Dich zu Brei.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlagen %s zu Brei.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 76..100:
+        tell_object(enemy,sprintf("  Ihr pulverisiert %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s pulverisieren Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s pulverisieren %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 101..150:
+        tell_object(enemy,sprintf("  Ihr zerstaeubt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s zerstaeuben Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerstaeuben %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 151..200:
+        tell_object(enemy,sprintf("  Ihr atomisiert %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s atomisieren Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s atomisieren %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      default:
+        tell_object(enemy,sprintf("  Ihr vernichtet %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s vernichten Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s vernichten %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      }
+
+    }
+    else {
+      switch (dam) {
+      case 0:
+        tell_object(enemy,sprintf("  Du verfehlst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s verfehlt Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s verfehlt %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 1:
+        tell_object(enemy,sprintf("  Du kitzelst %s am Bauch.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s kitzelt Dich am Bauch.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kitzelt %s am Bauch.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 2..3:
+        tell_object(enemy,sprintf("  Du kratzt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s kratzt Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kratzt %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 4..5:
+        tell_object(enemy,sprintf("  Du triffst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s trifft Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s trifft %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 6..10:
+        tell_object(enemy,sprintf("  Du triffst %s hart.\n",myname));
+        tell_object(this_object(),sprintf("  %s trifft Dich hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s trifft %s hart.\n",enname,myname),
+            ({ enemy, this_object() }));
+        break;
+      case 11..20:
+        tell_object(enemy,sprintf("  Du triffst %s sehr hart.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s trifft Dich sehr hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s trifft %s sehr hart.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 21..30:
+        tell_object(enemy,
+          sprintf("  Du schlaegst %s mit dem Krachen brechender Knochen.\n",
+                  myname));
+        tell_object(this_object(),
+          sprintf("  %s schlaegt Dich mit dem Krachen brechender Knochen.\n",
+                  enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlaegt %s mit dem Krachen brechender Knochen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 31..50:
+        tell_object(enemy,
+          sprintf("  Du zerschmetterst %s in kleine Stueckchen.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s zerschmettert Dich in kleine Stueckchen.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerschmettert %s in kleine Stueckchen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 51..75:
+        tell_object(enemy,sprintf("  Du schlaegst %s zu Brei.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s schlaegt Dich zu Brei.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlaegt %s zu Brei.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 76..100:
+        tell_object(enemy,sprintf("  Du pulverisierst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s pulverisiert Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s pulverisiert %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 101..150:
+        tell_object(enemy,sprintf("  Du zerstaeubst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s zerstaeubt Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerstaeubt %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 151..200:
+        tell_object(enemy,sprintf("  Du atomisierst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s atomisiert Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s atomisiert %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      default:
+        tell_object(enemy,sprintf("  Du vernichtest %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s vernichtet Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s vernichtet %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      }
+    }
+  }
+
+  // Man kann auch selbst-definierte Schadensmeldungen ausgeben lassen
+  else if( spell[SP_SHOW_DAMAGE] && pointerp(dam_msg) )
+  {
+    for( i=sizeof(dam_msg) ; --i >= 0 ; )
+    {
+      if ( dam>dam_msg[i][0] )
+      {
+        tell_object(ME,mess(dam_msg[i][1],ME,enemy));
+        tell_object(enemy,mess(dam_msg[i][2],ME,enemy));
+        say(mess(dam_msg[i][3],ME,enemy), enemy);
+        break;
+      }
+    }
+  }
+  // else (!spell[SP_SHOW_DAMAGE]) keine Schadensmeldung.
+
+  // Informationen ueber den letzten Angriff festhalten
+  Set(P_LAST_DAMTYPES, dam_type);
+  Set(P_LAST_DAMTIME,  time());
+  Set(P_LAST_DAMAGE,   dam);
+
+  // Bei Angriffen mit SP_NO_ENEMY-Flag kann man nicht sterben ...
+  if ( spell[SP_NO_ENEMY] )
+    reduce_hit_points(dam);
+  // ... bei allen anderen natuerlich schon
+  else
+    do_damage(dam,enemy);
+
+  // evtl. ist dies Objekt hier tot...
+  if (!objectp(ME)) return dam;
+
+  // Testen, ob man in die Fucht geschlagen wird
+  CheckWimpyAndFlee();
+
+  // Verursachten Schaden (in LP) zurueckgeben
+  return dam;
+}
+
+public float CheckResistance(string *dam_type) {
+    //funktion kriegt die schadensarten uebergeben, schaut sich dieses
+    //sowie P_RESISTENCE_STRENGTH und P_RESITENCE_MODFIFIER an und berechnet
+    //einen Faktor, mit dem die urspruengliche Schadensmenge multipliziert
+    //wird, um den Schaden nach Beruecksichtigung der Resis/Anfaelligkeiten zu
+    //erhalten. Rueckgabewert normalerweise (s.u.) >=0.
+  mapping rstren, mod;
+  float   faktor,n;
+  int     i;
+
+  mod = Query(P_RESISTANCE_MODIFIER);
+  if ( mappingp(mod) )
+    mod = mod["me"];
+
+  if ( !mappingp(rstren=Query(P_RESISTANCE_STRENGTHS)) )
+  {
+    if (!mappingp(mod))
+      return 1.0;
+    else
+      rstren = ([]);
+  }
+
+  if ( !mappingp(mod) )
+    mod = ([]);
+
+  if ( (i=sizeof(dam_type))<1 )
+    return 1.0;
+
+  n=to_float(i);
+  faktor=0.0;
+
+  //dies hier tut nicht mehr das gewuenschte, wenn in P_RESISTENCE_STRENGTHS
+  //Faktoren <-1.0 angegeben werden. Rueckgabewerte <0 sind dann moeglich und
+  //leider werden die Schadensarten ohne Resis nicht mehr richtig verwurstet. :-/
+  foreach(string dt: dam_type) {
+    faktor = faktor + (1.0 + to_float(rstren[dt]))
+           * (1.0 + to_float(mod[dt]))-1.0;
+  }
+  return 1.0+(faktor/n);
+}
+
+public varargs mapping StopHuntingMode(int silent)
+{ mapping save_enemy;
+  int     i;
+
+  save_enemy=enemies;
+  if ( !silent )
+    walk_mapping(enemies, #'StopHuntText); //#');
+
+  enemies=m_allocate(0,1);
+  last_attack_msg=0;
+
+  return save_enemy;
+}
+
+public <object*|int*>* QueryEnemies()
+{
+  return ({m_indices(enemies),m_values(enemies)});
+}
+
+public mapping GetEnemies()
+{
+  return enemies;
+}
+
+public mapping SetEnemies(<object*|int*>* myenemies)
+{
+  enemies=mkmapping(myenemies[0],myenemies[1]);
+  return enemies;
+}
+
+private string _kill_alias( string str )
+{
+    return "\\" + str;
+}
+
+public varargs void Flee( object oldenv, int force )
+{ mixed   *exits, exit, dex;
+  mapping tmp;
+  int     i;
+  object  env;
+
+  if ( !environment() )
+    return;
+
+  // mit 'force' kann man die Checks umgehen, damit der Spieler auf jeden
+  // Fall fluechtet ...
+  if ( !force && ( oldenv && (oldenv != environment()) ||
+                   query_once_interactive(ME) && (!EnemyPresent() ||
+                   (QueryProp(P_HP) >= QueryProp(P_WIMPY))) ) )
+    return;
+
+  // ... aber Magier fluechten zu lassen ist nicht ganz so einfach ;-)
+  if ( query_once_interactive(this_object()) && IS_LEARNING(this_object()) )
+    return;
+
+  // Geister brauchen nicht um ihr Leben zu fuerchten
+  if ( QueryProp(P_GHOST) )
+    return;
+
+  tell_object( ME, "Die Angst ist staerker als Du ... "+
+                   "Du willst nur noch weg hier.\n");
+
+  if ( TeamFlee() ) // Erfolgreiche Flucht in hintere Kampfreihe?
+    return;
+
+  env = environment();
+  tmp = environment()->QueryProp(P_EXITS);
+  exits = m_indices(tmp);
+  tmp = environment()->QueryProp(P_SPECIAL_EXITS);
+  exits += m_indices(tmp);
+
+  if ( query_once_interactive(ME) )
+        exits = map( exits, #'_kill_alias/*'*/ );
+
+  // die Fluchtrichtung von Magiern wird aus Sicherheitsgruenden
+  // nicht ausgewertet
+  if ( interactive(ME) && IS_SEER(ME) && !IS_LEARNER(ME)
+      && (dex=QueryProp(P_WIMPY_DIRECTION)) )
+  {
+    i = 60 + 4 * (QueryProp(P_LEVEL) - 30) / 3;
+    exits += ({dex}); // bevorzugte Fluchtrichtung mindestens einmal
+  }
+
+  if ( !sizeof(exits) )
+  {
+    tell_object( ME, "Du versuchst zu fliehen, schaffst es aber nicht.\n" );
+
+    return;
+  }
+
+  while ( sizeof(exits) && (environment()==env) )
+  {
+    if ( dex                         // Vorzugsweise Fluchtrichtung?
+        && (member(exits,dex) >= 0)  // moeglich?
+        && (random(100) <= i))       // und Wahrscheinlichkeit gross genug?
+      exit = dex;
+    else
+      exit = exits[random(sizeof(exits))];
+
+    catch(command(exit);publish);
+    exits -= ({exit});
+  }
+
+  if ( environment()==env )
+    tell_object( ME, "Dein Fluchtversuch ist gescheitert.\n" );
+}
+
+public object EnemyPresent()
+{
+  foreach(object en: enemies) {
+    if (environment()==environment(en))
+      return en;
+  }
+  return 0;
+}
+
+public object InFight()
+{
+  return EnemyPresent();
+}
+
+public varargs int StopHuntID(string str, int silent) { 
+
+  if ( !stringp(str) )
+    return 0;
+
+  int j;
+  foreach(object en: enemies) {
+    if (en->id(str)) {
+      StopHuntFor(en,silent);
+      j++;
+    }
+  }
+
+  return j;
+}
+
+public int SpellDefend(object caster, mapping sinfo)
+{ int    re;
+  mixed  res;
+  string *ind;
+
+  re = UseSkill(SK_SPELL_DEFEND,([ SI_SKILLARG : sinfo ,
+                                   SI_ENEMY    : caster ]) );
+
+  if ( (res=QueryProp(P_MAGIC_RESISTANCE_OFFSET)) && mappingp(res)
+      && pointerp(sinfo[SI_MAGIC_TYPE]))
+  {
+    ind = m_indices(res) & sinfo[SI_MAGIC_TYPE];
+
+    if (pointerp(ind) && sizeof(ind) ) {
+      foreach(string index : ind)
+        re+=res[index];
+    }
+  }
+  else if(res && intp(res))
+    re+=res;
+
+  if ( (re>3333) && query_once_interactive(this_object()) )
+    re=3333; /* Maximal 33% Abwehrchance bei Spielern */
+  return re;
+}
+
+// **** this is property-like
+
+static int _set_attack_busy(mixed val)
+{
+  if ( ((to_int(val))>5) && previous_object(1)
+      && query_once_interactive(previous_object(1)) )
+    log_file("ATTACKBUSY",sprintf("%s %d Taeter: %O Opfer: %O\n",
+             dtime(time()),to_int(val),previous_object(1),this_object()));
+
+  attack_busy-=to_int(val*100.0);
+
+  if ( attack_busy<-2000 )
+    attack_busy=-2000;
+
+  return attack_busy;
+}
+
+static int _query_attack_busy()
+{
+  if (IS_LEARNING(ME))
+    return 0;
+
+  return (attack_busy<100);
+}
+
+// **** local property methods
+static int _set_wimpy(int i)
+{
+  if ( !intp(i) || (i>QueryProp(P_MAX_HP)) || (i<0) )
+    return 0;
+
+  // ggf. Statusreport ausgeben
+  if (interactive(ME))
+    status_report(DO_REPORT_WIMPY, i);
+
+  return Set(P_WIMPY, i);
+}
+
+static string _set_wimpy_dir(string s) {
+  // ggf. Statusreport ausgeben
+  if (interactive(ME))
+    status_report(DO_REPORT_WIMPY_DIR, s);
+  return Set(P_WIMPY_DIRECTION, s, F_VALUE);
+}
+
+static mixed _set_hands(mixed h)
+{ 
+  if ( sizeof(h)==2 )
+    h += ({ ({DT_BLUDGEON}) });
+  if (!pointerp(h[2]))
+    h[2] = ({h[2]});
+  return Set(P_HANDS, h, F_VALUE);
+}
+
+//TODO normalisieren/korrigieren in updates_after_restore().
+static mixed _query_hands()
+{
+  mixed *hands = Query(P_HANDS);
+  if ( !hands )
+    return Set(P_HANDS, ({ " mit blossen Haenden", 30, ({DT_BLUDGEON})}));
+  else if ( sizeof(hands)<3 )
+    return Set(P_HANDS, ({hands[0], hands[1], ({DT_BLUDGEON})}));
+  else if ( !pointerp(hands[2]) )
+    return Set(P_HANDS, ({hands[0], hands[1], ({ hands[2] })}));
+
+  return Query(P_HANDS);
+}
+
+static int _query_total_wc()
+{ mixed res;
+  int   totwc;
+
+  if ( objectp(res=QueryProp(P_WEAPON)) )
+    totwc = ((int)(res->QueryProp(P_WC)));
+  else if ( pointerp(res=QueryProp(P_HANDS)) && sizeof(res)>1
+           && intp(res[1]) )
+    totwc=((int)res[1]);
+  else
+    totwc=30;
+
+  totwc = ((2*totwc)+(10*QueryAttribute(A_STR)))/3;
+
+  return Set(P_TOTAL_WC, totwc, F_VALUE);
+}
+
+static int _query_total_ac() {
+
+  int totac = 0;
+  object *armours = QueryProp(P_ARMOURS);
+  object parry = QueryProp(P_PARRY_WEAPON);
+
+  if ( member(armours,0)>=0 ) {
+    armours -= ({ 0 }); 
+  }
+
+  foreach(object armour: armours)
+    totac += ((int)armour->QueryProp(P_AC));
+
+  if ( objectp(parry) )
+    totac += parry->QueryProp(P_AC);
+
+  totac += (QueryProp(P_BODY)+QueryAttribute(A_DEX));
+
+  return Set(P_TOTAL_AC, totac, F_VALUE);
+}
+
+static mapping _query_resistance_strengths() {
+
+  UpdateResistanceStrengths();
+
+  mapping rstren = copy(Query(P_RESISTANCE_STRENGTHS, F_VALUE));
+  mapping mod = Query(P_RESISTANCE_MODIFIER, F_VALUE);
+
+  if ( !mappingp(rstren) )
+    rstren = ([]);
+
+  if ( !mappingp(mod) || !mappingp(mod=mod["me"]) || !sizeof(mod) )
+    return rstren;
+
+  foreach(string modkey, float modvalue : mod)
+    rstren[modkey] = ((1.0+rstren[modkey])*(1.0+modvalue))-1.0;
+
+  return rstren;
+}
+
+static int _set_disable_attack(int val)
+{
+  if (val<-100000)
+           {
+                  log_file("PARALYSE_TOO_LOW",
+                          sprintf("Wert zu klein: %s, Wert: %d, "
+                                  "Aufrufer: %O, Opfer: %O",
+                                  ctime(time()),
+                                          val,previous_object(1),
+                                          this_object()));
+           }
+  if ( val>30 )
+    val=30;
+  if ( (val>=20) && previous_object(1)!=ME && query_once_interactive(ME) )
+    log_file("PARALYSE",sprintf("%s %d Taeter: %O Opfer: %O\n",
+                                ctime(time())[4..15],
+                                val,previous_object(1),this_object()));
+ 
+  if (time()<QueryProp(P_NEXT_DISABLE_ATTACK))
+  {
+    // gueltige Zeitsperre existiert.
+    // Erhoehen von P_DISABLE_ATTACK geht dann nicht. (Ja, auch nicht erhoehen
+    // eines negativen P_DISABLE_ATTACK)
+    if (val >= QueryProp(P_DISABLE_ATTACK))
+      return DISABLE_TOO_EARLY;
+    // val ist kleiner als aktuelles P_DISABLE_ATTACK - das ist erlaubt, ABER es
+    // darf die bestehende Zeitsperre nicht verringern, daher wird sie nicht
+    // geaendert.
+    return Set(P_DISABLE_ATTACK,val,F_VALUE);
+  }
+  // es existiert keine gueltige Zeitsperre - dann wird sie nun gesetzt.
+  // (Sollte val < 0 sein, wird eine Zeitsperre in der Vergangenheit gesetzt,
+  // die schon abgelaufen ist. Das ist ueberfluessig, schadet aber auch
+  // nicht.)
+  SetProp(P_NEXT_DISABLE_ATTACK,time()+val*2*__HEART_BEAT_INTERVAL__);
+  return Set(P_DISABLE_ATTACK,val);
+}
+
+// Neue Verwaltung der Haende:
+
+// P_HANDS_USED_BY enhaelt ein Array mit allen Objekten, die Haende
+// belegen, jedes kommt so oft vor wie Haende belegt werden.
+
+static mixed *_query_hands_used_by()
+{
+  return ((Query(P_HANDS_USED_BY, F_VALUE) || ({}) ) - ({0}));
+}
+
+static int _query_used_hands()
+{
+  return sizeof(QueryProp(P_HANDS_USED_BY));
+}
+
+static int _query_free_hands()
+{
+  return (QueryProp(P_MAX_HANDS)-QueryProp(P_USED_HANDS));
+}
+
+public varargs int UseHands(object ob, int num)
+{ mixed *h;
+
+  if ( !ob && !(ob=previous_object(1)) )
+    return 0;
+
+  if ( (num<=0) && ((num=ob->QueryProp(P_HANDS))<=0) )
+    return 0;
+
+  h=QueryProp(P_HANDS_USED_BY)-({ob});
+
+  if ( (sizeof(h)+num)>QueryProp(P_MAX_HANDS) )
+    return 0;
+
+  foreach(int i: num)
+    h+=({ob});
+
+  SetProp(P_HANDS_USED_BY,h);
+
+  return 1;
+}
+
+public varargs int FreeHands(object ob)
+{
+  if ( !ob && !(ob=previous_object(1)) )
+    return 0;
+
+  SetProp(P_HANDS_USED_BY,QueryProp(P_HANDS_USED_BY)-({ob}));
+
+  return 1;
+}
+
+// Kompatiblitaetsfunktionen:
+
+static int _set_used_hands(int new_num)
+{ int    old_num, dif;
+  object ob;
+
+  old_num=QueryProp(P_USED_HANDS);
+
+  if ( !objectp(ob=previous_object(1)) )
+    return old_num;
+
+  // Meldung ins Debuglog schreiben. Aufrufer sollte gefixt werden.
+  debug_message(sprintf("P_USED_HANDS in %O wird gesetzt durch %O\n",
+        this_object(), ob), DMSG_LOGFILE|DMSG_STAMP);
+
+  if ( !(dif=new_num-old_num) )
+    return new_num;
+
+  if ( dif>0 )
+    UseHands(ob,dif);
+  else
+    FreeHands(ob);
+
+  return QueryProp(P_USED_HANDS);
+}
+
+// Funktionen fuer Begruessungsschlag / Nackenschlag:
+
+public int CheckEnemy(object ob)
+{
+  return (living(ob) && IsEnemy(ob));
+}
+
+public varargs void ExecuteMissingAttacks(object *remove_attackers)
+{
+  if ( !pointerp(missing_attacks) )
+    missing_attacks=({});
+
+  if ( pointerp(remove_attackers) )
+    missing_attacks-=remove_attackers;
+
+  foreach(object ob : missing_attacks) {
+    if ( objectp(ob) && (environment(ob)==environment()) )
+      ob->Attack2(ME);
+  }
+  missing_attacks=({});
+}
+
+public void InitAttack()
+{ object ob,next;
+  closure cb;
+
+  if ( !living(ME) )
+    return;
+
+  ExecuteMissingAttacks();
+  //EMA kann das Living zerstoeren oder toeten...
+  if (!living(ME) || QueryProp(P_GHOST)) return;
+
+  if ( objectp(ob=IsTeamMove()) )
+    cb=symbol_function("InitAttack_Callback",ob);
+  else
+    cb=0;
+
+  for ( ob=first_inventory(environment()) ; objectp(ob) ; ob=next)
+  {
+    next=next_inventory(ob);
+
+    if ( !living(ob) )
+      continue;
+
+    if (ob->IsEnemy(ME))
+    {
+      // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
+      // aktualisiert und b) werden Teammitglieder von mir bei diesem
+      // InsertEnemy() ggf. erfasst.
+      ob->InsertEnemy(ME);
+
+      if ( closurep(cb) && funcall(cb,ob) ) // Wird ganzes Team gemoved?
+        missing_attacks += ({ ob }); // Dann erstmal warten bis alle da sind
+      else
+        ob->Attack2(ME);
+
+    }
+    else if ( IsEnemy(ob) )
+    {
+      // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
+      // aktualisiert und b) werden Teammitglieder von ob bei diesem 
+      // InsertEnemy() ggf. erfasst.
+      InsertEnemy(ob);
+      Attack2(ob);
+    }
+    //Attack2 kann dieses Objekt zerstoeren oder toeten. Wenn ja: abbruch
+    if ( !living(ME) || QueryProp(P_GHOST)) break;
+  }
+}
+
+public void ExitAttack()
+{
+  if ( !living(ME) )
+    return;
+
+  // Noch nachzuholende Begruessungsschlaege:
+  ExecuteMissingAttacks();
+}
+
+public object|object*|mapping QueryArmourByType(string type)
+{
+  // Rueckgabewert:
+  // DIE Ruestung vom Typ <type>, falls <type> nicht AT_MISC,
+  // Array aller AT_MISC-Ruestungen falls <type> AT_MISC (auch leer),
+  // Mapping mit allen oben genannten Infos, falls <type> Null
+
+  object *armours;
+  string typ2;
+
+  // Wenn Cache vorhanden, dann Cache liefern.
+  if (mappingp(QABTCache)) {
+    if (type == AT_MISC)
+      return QABTCache[AT_MISC] - ({0});
+    else if (type)
+      return QABTCache[type];
+    else
+      return copy(QABTCache);
+  }
+
+  if ( !pointerp(armours=QueryProp(P_ARMOURS)) )
+    armours=({});
+
+  // Cache erzeugen
+  QABTCache = ([ AT_MISC: ({}) ]);
+  foreach(object ob: armours - ({0}) ) {
+    if ( !stringp(typ2=ob->QueryProp(P_ARMOUR_TYPE)) )
+      continue;
+    if ( typ2==AT_MISC )
+      QABTCache[AT_MISC] += ({ob});
+    else
+      QABTCache[typ2] = ob;
+  }
+  // Und gewuenschtes Datum liefern.
+  if (type)
+    return QABTCache[type];
+  else
+    return copy(QABTCache);
+}
+
+//TODO: langfristig waers besser, wenn hier nicht jeder per SetProp() drauf
+//los schreiben wuerde.
+static object *_set_armours(object *armours) {
+  if (pointerp(armours)) {
+    // Cache wegwerfen
+    QABTCache = 0;
+    // armours ggf. unifizieren. Ausserdem Kopie reinschreiben.
+    return Set(P_ARMOURS, m_indices(mkmapping(armours)), F_VALUE);
+  }
+  return QueryProp(P_ARMOURS);
+}
+
+/** Reduziert die Befriedezaehler pro Reset im Durchschnitt um 2.5.
+  Berechnet ganzzahlige durchschnittliche Resets seit dem letzten Expire und
+  erhoeht die letzte Expirezeit um Resets*__RESET_TIME__ (Standard Intervall
+  fuer Reset ist momentan 3600s, im Durchschnitt kommen dann 2700 zwischen 2
+  Resets bei raus). So wird der unganzzahlige Rest beim naechsten Aufruf
+  beruecksichtigt.
+  Diese Variante des Expires wurde gewaehlt, um zu vermeiden, combat.c einen
+  reset() zu geben und in jedem Reset in jedem Lebewesen ein Expire machen zu
+  muessen, auch wenn nur in sehr wenigen Lebewesen was gemacht werden muss.
+  @param[in,out] ph Mapping aus P_PEACE_HISTORY. Wird direkt aktualisiert.
+  @attention Muss ein gueltiges P_PEACE_HISTORY uebergeben bekommen, anderem
+  Datentyp inkl. 0 wird die Funktion buggen.
+  */
+private void _decay_peace_history(mixed ph) {
+  // Ganzzahlige resets seit letztem Expire ermitteln. (Durchschnitt)
+  int resets = (time() - ph[0]) / (__RESET_TIME__*75/100);
+  // auf Zeitstempel draufrechnen, damit der unganzzahlige Rest nicht
+  // verlorengeht, der wird beim naechsten Expire dann beruecksichtigt.
+  ph[0] += resets * (__RESET_TIME__ * 75 / 100);
+  // pro Reset werden im Durchschnitt 2.5 Versuche abgezogen. (Hier werden
+  // beim Expire mal 2 und mal 3 Versuche pro Reset gerechnet. Aber falls hier
+  // viele Resets vergangen sind, ist es vermutlich eh egal, weil die Counter
+  // auf 0 fallen.)
+  int expire = resets * (random(2) + 2);
+  // ueber alle Gilden
+  mapping tmp=ph[1];
+  foreach(string key, int count: &tmp ) {
+    count-=expire;
+    if (count < 0) count = 0;
+  }
+}
+
+/** Pacify() dient zur Bestimmung, ob sich ein Lebewesen gerade 
+ * befrieden lassen will.
+ * Berechnet eine Wahrscheinlichkeit nach unten stehender Formel, welche die
+ * Intelligenz dieses Lebenwesens, die Intelligenz des Casters und die
+ * bisherige Anzahl erfolgreicher Befriedungsversuche dieser Gilde eingeht.
+ * Anschliessend wird aus der Wahrscheinlichkeit und random() bestimmt, ob
+ * dieser Versuch erfolgreich ist.
+Formel: w = (INT_CASTER + 10 - ANZ*4) / (INT_ME + 10)
+INT_CASTER: Caster-Intelligenz, INT_ME: Intelligenz dieses Livings
+ANZ: Anzahl erfolgreicher Befriedungsversuche
+
+Annahme: INT_CASTER === 22, alle Wahrscheinlichkeiten * 100
+
+INT_ME   Erfolgswahrscheinlichkeiten je nach Anzahl erfolgreicher Versuche
+              1       2       3       4       5       6       7       8
+      0     280     240     200     160     120      80      40       0
+      2  233,33     200  166,67  133,33     100   66,67   33,33       0
+      4     200  171,43  142,86  114,29   85,71   57,14   28,57       0
+      6     175     150     125     100      75      50      25       0
+      8  155,56  133,33  111,11   88,89   66,67   44,44   22,22       0
+     10     140     120     100      80      60      40      20       0
+     12  127,27  109,09   90,91   72,73   54,55   36,36   18,18       0
+     14  116,67     100   83,33   66,67      50   33,33   16,67       0
+     16  107,69   92,31   76,92   61,54   46,15   30,77   15,38       0
+     18     100   85,71   71,43   57,14   42,86   28,57   14,29       0
+     20   93,33      80   66,67   53,33      40   26,67   13,33       0
+     22    87,5      75    62,5      50    37,5      25    12,5       0
+     24   82,35   70,59   58,82   47,06   35,29   23,53   11,76       0
+     26   77,78   66,67   55,56   44,44   33,33   22,22   11,11       0
+     28   73,68   63,16   52,63   42,11   31,58   21,05   10,53       0
+     30      70      60      50      40      30      20      10       0
+     32   66,67   57,14   47,62    38,1   28,57   19,05    9,52       0
+     34   63,64   54,55   45,45   36,36   27,27   18,18    9,09       0
+     35   62,22   53,33   44,44   35,56   26,67   17,78    8,89       0
+     36   60,87   52,17   43,48   34,78   26,09   17,39     8,7       0
+     38   58,33      50   41,67   33,33      25   16,67    8,33       0
+     40      56      48      40      32      24      16       8       0
+     42   53,85   46,15   38,46   30,77   23,08   15,38    7,69       0
+     44   51,85   44,44   37,04   29,63   22,22   14,81    7,41       0
+     46      50   42,86   35,71   28,57   21,43   14,29    7,14       0
+     48   48,28   41,38   34,48   27,59   20,69   13,79     6,9       0
+     50   46,67      40   33,33   26,67      20   13,33    6,67       0
+     52   45,16   38,71   32,26   25,81   19,35    12,9    6,45       0
+     54   43,75    37,5   31,25      25   18,75    12,5    6,25       0
+     56   42,42   36,36    30,3   24,24   18,18   12,12    6,06       0
+     58   41,18   35,29   29,41   23,53   17,65   11,76    5,88       0
+     60      40   34,29   28,57   22,86   17,14   11,43    5,71       0
+     62   38,89   33,33   27,78   22,22   16,67   11,11    5,56       0
+     64   37,84   32,43   27,03   21,62   16,22   10,81    5,41       0
+     66   36,84   31,58   26,32   21,05   15,79   10,53    5,26       0
+     68    35,9   30,77   25,64   20,51   15,38   10,26    5,13       0
+     70      35      30      25      20      15      10       5       0
+     72   34,15   29,27   24,39   19,51   14,63    9,76    4,88       0
+     74   33,33   28,57   23,81   19,05   14,29    9,52    4,76       0
+     76   32,56   27,91   23,26    18,6   13,95     9,3    4,65       0
+     78   31,82   27,27   22,73   18,18   13,64    9,09    4,55       0
+     80   31,11   26,67   22,22   17,78   13,33    8,89    4,44       0
+     82   30,43   26,09   21,74   17,39   13,04     8,7    4,35       0
+     84   29,79   25,53   21,28   17,02   12,77    8,51    4,26       0
+     86   29,17      25   20,83   16,67    12,5    8,33    4,17       0
+     88   28,57   24,49   20,41   16,33   12,24    8,16    4,08       0
+     90      28      24      20      16      12       8       4       0
+     92   27,45   23,53   19,61   15,69   11,76    7,84    3,92       0
+     94   26,92   23,08   19,23   15,38   11,54    7,69    3,85       0
+     96   26,42   22,64   18,87   15,09   11,32    7,55    3,77       0
+     98   25,93   22,22   18,52   14,81   11,11    7,41     3,7       0
+    100   25,45   21,82   18,18   14,55   10,91    7,27    3,64       0
+ * @return 1, falls Befrieden erlaubt ist, 0 sonst.
+ * @param[in] caster Derjenige, der den Spruch ausfuehrt.
+ * @attention Wenn acify() 1 zurueckliefert, zaehlt dies als
+ * erfolgreicher Befriedungsversuch und in diesem Lebewesen wurde
+ * StopHuntingMode(1) aufgerufen.
+*/
+public int Pacify(object caster) {
+  
+  // wenn das Viech keine Gegner hat, dann witzlos. ;-) Ohne Caster gehts auch
+  // direkt raus.
+  if (!mappingp(enemies) || !sizeof(enemies)
+      || !objectp(caster)) {
+    return 0;
+  }
+
+  // Wenn P_ACCEPT_PEACE gesetzt ist, altes Verhalten wiederspiegeln
+  // -> der NPC ist einfach immer befriedbar. Gleiches gilt fuer den Caster
+  // selber, der wird sich ja nicht gegen das eigene Befriede wehren. Und auch
+  // im team wehrt man sich nicht gegen das Befriede eines Teamkollegen
+  if (QueryProp(P_ACCEPT_PEACE)==1 || caster==ME
+      || member(TeamMembers(), caster) > -1) {
+    StopHuntingMode(1); // Caster/Gilde sollte eigene Meldung ausgeben
+    return 1;
+  }
+
+  string gilde = caster->QueryProp(P_GUILD) || "ANY";
+
+  // ggf. P_PEACE_HISTORY initialisieren
+  mixed ph = (mixed)QueryProp(P_PEACE_HISTORY);
+  if (!pointerp(ph))
+    SetProp(P_PEACE_HISTORY, ph=({time(), ([]) }) );
+  
+  // ggf. die Zaehler reduzieren.
+  if ( ph[0] + __RESET_TIME__ * 75/100 < time()) {
+    _decay_peace_history(&ph);
+  }
+  
+  float w = (caster->QueryAttribute(A_INT) + 10 - ph[1][gilde] * 4.0) / 
+             (QueryAttribute(A_INT) + 10);
+  // auf [0,1] begrenzen.
+  if (w<0) w=0.0;
+  else if (w>1) w=1.0;
+  // w * n ist eine Zahl zwischen 0 und n, wenn w * n > random(n)+1,
+  // darf befriedet werden. Da das Random fuer grosse Zahlen
+  // besser verteilt ist, nehm ich n = __INT_MAX__ und vernachlaessige
+  // ausserdem die +1 beim random().
+  if (ceil(w * __INT_MAX__) > random(__INT_MAX__) ) {
+    ph[1][gilde]++;
+    StopHuntingMode(1);
+    return 1;
+  }
+  // ein SetProp(P_PEACE_HISTORY) kann entfallen, da das Mapping direkt
+  // geaendert wird. Sollte die Prop allerdings mal ne Querymethode haben,
+  // welche eine Kopie davon liefert, muss das hier geaendert oder die
+  // Prop per Query() abgefragt werden.
+  return 0;
+}
+
diff --git a/std/living/comm.c b/std/living/comm.c
new file mode 100644
index 0000000..6001ffd
--- /dev/null
+++ b/std/living/comm.c
@@ -0,0 +1,133 @@
+// MorgenGrauen MUDlib
+//
+// living/comm.c -- communiction module for livings
+//
+// $Id$
+
+#pragma strong_types,save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#include <defines.h>
+#include <living/comm.h>
+
+void create_super()
+{
+  set_next_reset(-1);
+}
+
+protected string comm_guess_action() {
+  string cmd;
+  string action = query_verb();
+  // Die Aktionen sind intern in der Regel nach den haeufigsten Kommandoverben
+  // dieser Aktion benannt. Bei einigen Aktionen sind mehrere Kommandoverben
+  // ueblich, die sollen hier noch abgehandelt werden.
+  switch(action) {
+    case "nehme":
+      // MA_TAKE == nimm
+      action = MA_TAKE;
+      break;
+
+    case "norden":
+    case "nordosten":
+    case "osten":
+    case "suedosten":
+    case "sueden":
+    case "suedwesten":
+    case "westen":
+    case "nordwesten":
+    case "oben":
+    case "unten":
+    case "betrete":
+    case "verlasse":
+    case "teleport":
+    case "teleportiere":
+      action = MA_MOVE;
+      break;
+
+    case "unt":
+      action = MA_LOOK;
+      break;
+
+    case "wirf":
+      if (strstr(query_command(), " weg") > -1)
+        action = MA_PUT;
+      break;
+
+    case "stecke":
+      cmd = query_command();
+      if (strstr(cmd, " weg") > -1)
+        action = MA_UNWIELD;
+      else if (strstr(cmd," in ") > -1)
+        action = MA_PUT;
+      break;
+
+    case "ziehe":
+      cmd = query_command();
+      if (strstr(cmd, " an") > -1)
+        action = MA_WEAR;
+      else if (strstr(cmd, " aus") > -1)
+        action = MA_UNWEAR;
+      break;
+
+    case "esse":
+    case "friss":
+      action = MA_EAT;
+      break;
+
+    case "saufe":
+      action = MA_DRINK;
+      break;
+
+    case "hoere":
+      //MA_LISTEN == lausche
+      action = MA_LISTEN;
+      break;
+    case "lese":
+      action = MA_READ;
+      break;
+
+    case ":":
+    case ";":
+      action = MA_EMOTE;
+      break;
+
+    case "zerbreche":
+    case "zerstoere":
+    case "verbrenne":
+    case "entsorge":
+      action = MA_REMOVE;
+      break;
+  }
+  return action;
+}
+
+protected int comm_guess_message_type(string action, mixed origin) {
+  // everything not mentioned in the switch becomes MT_LOOK.
+  switch(action) {
+    case MA_FIGHT:
+      // Kampf kann man meisten sowohl sehen als auch hoeren.
+      return MT_LOOK | MT_LISTEN;
+    case MA_LISTEN:
+    case MA_SAY:
+      return MT_LISTEN;
+    case MA_FEEL:
+      return MT_FEEL;
+    case MA_SMELL:
+      return MT_SMELL;
+    case MA_CHANNEL:
+      return MT_COMM | MT_FAR;
+    case MA_EMOTE:
+      if (objectp(origin)
+          && environment(origin) == environment())
+        return MT_COMM;
+      else
+        return MT_COMM | MT_FAR;
+    case MA_SHOUT:
+      return MT_LISTEN | MT_FAR;
+  }
+  // die meisten Aktionen sind zumindest sichtbar...
+  return MT_LOOK;
+}
+
diff --git a/std/living/description.c b/std/living/description.c
new file mode 100644
index 0000000..6353ca9
--- /dev/null
+++ b/std/living/description.c
@@ -0,0 +1,332 @@
+// MorgenGrauen MUDlib
+//
+// living/description.c -- description for living objects
+//
+// $Id: description.c 9395 2015-12-08 23:04:38Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/description";
+
+#define NEED_PROTOTYPES
+
+#include <living/skills.h>
+#include <living/clothing.h>
+#include <thing/properties.h>
+#include <wizlevels.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <class.h>
+#include <sys_debug.h>
+
+public string _query_internal_extralook() {
+  mixed xl;
+  int zeit;
+  string res, look="";
+
+  xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE);
+  if (!mappingp(xl))
+    return(0);
+
+  foreach(string key, mapping xld: xl) {
+    if (intp(zeit=xld["xlduration"])) {
+      //hat offenbar nen Ablaufdatum
+      if ( (zeit > 0 && zeit < time()) ||
+           (zeit < 0 && abs(zeit) < object_time(ME)) ) {
+        // Zeit abgelaufen oder
+        // negative "Ablaufzeit" und das Objekt ist neuer als die
+        // Eintragzeit, also loeschen und weiter (ja, das geht. ;-) und xld
+        // hat das Eintragsmapping ja noch, weitere Benutzung also moeglich.)
+        m_delete(xl,key);
+        // ggf. Meldung ausgeben
+        if (interactive(ME)) {
+          if (sizeof(xld["xlende"])) {
+            tell_object(ME,xld["xlende"]);
+          }
+          //kein einfacher String, aber Objekt+Funktion gegeben?
+          else if (sizeof(xld["xlendefun"]) && sizeof(xld["xlobjectname"]) &&
+            (!catch(res=call_other(xld["xlobjectname"],xld["xlendefun"],ME)
+                    ;publish))) {
+              if (stringp(res) && sizeof(res))
+                tell_object(ME,res);
+            }
+        }
+        continue;
+      }
+    }
+    // Der Eintrag ist offenbar noch gueltig, Meldung anhaengen, bzw. via
+    // Funktionsaufruf beschaffen.
+    if (sizeof(xld["xllook"]))
+      look+=xld["xllook"];
+    else if (sizeof(xld["xlfun"]) && sizeof(xld["xlobjectname"])) {
+      closure cl;
+      if (catch(cl=symbol_function(xld["xlfun"],xld["xlobjectname"]);publish)
+          || !cl) {
+          // wenn Fehler beim Laden/Closure erstellen, dann Eintrag loeschen
+          // -> Annahme, dass dieser Fehler permanent sein wird, z.B. Eintrag
+          // von Clonen
+          m_delete(xl,key);
+          continue;
+      }
+      else if (!catch(res=funcall(cl, ME); publish)) {
+        if (!stringp(res) || !sizeof(res)) {
+          // keinen String oder leeren String gekriegt -> ueberspringen.
+          continue;
+        }
+        else
+          look+=res;
+      }
+    }
+  }
+  // fertig. Wenn look nicht leer ist, zurueckgeben, sonst 0.
+  if (sizeof(look))
+    return(look);
+  else
+    return(0);
+}
+
+public varargs int AddExtraLook(string look, int duration, string key, 
+                                string lookende, object ob) {
+  mapping xl;
+  string oname;
+  if (!stringp(key) || !sizeof(key)) {
+    // Automatisch erzeugen, wenn moeglich
+    if (!objectp(previous_object()) || 
+        !stringp(key=object_name(previous_object())) || !sizeof(key))
+      return(-1);
+  }
+
+  if (!stringp(look) || !sizeof(look))
+    return(-2);
+  if (!intp(duration))
+    return(-3);
+
+  xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE); // dran denken: liefert referenz zurueck
+  if (!mappingp(xl)) {
+    Set(P_INTERNAL_EXTRA_LOOK, xl=([]) );
+  }
+
+  // kein Automatisches Ueberschreiben.
+  if (member(xl,key))
+    return(-4);
+
+  // neg. Werte: "bis Ende/reboot", abs(duration) == Eintragzeit
+  // 0: "fuer ewig", >0: Zeitdauer in Sekunden
+  if (duration > 0)
+    duration+=time();  // hoffentlich gibt es reichtzeitig 64bit-Ints
+  else if (duration < 0)
+    duration=negate(time());
+  // 0 bleibt, wie es ist.
+
+  if (objectp(ob)) {
+    // Funktionsname und Objektname (als Name, damit es auch noch geht, wenn
+    // das Objekt entladen wurde, Crash/reboot war etc.) speichern
+    // Clone werden auch gespeichert, aber es wird direkt ein harter Fehler
+    // ausgeloest, wenn ein permanenter Xlook fuer einen Clone registriert
+    // werden soll: das kann nicht gehen.
+    if (!duration && clonep(ob))
+        raise_error(sprintf(
+           "AddExtraLook(): Fehlerhaftes Argument <duration>: %d, "
+           "permanente Extralooks durch Clone (%s) nicht registrierbar.\n",
+           duration, object_name(ob)));
+
+    xl[key]=(["xlobjectname":object_name(ob),
+              "xlfun": look,
+             ]);
+    // ggf. Name der Funktion speichern, die bei Ablauf aufgerufen wird.
+    if (stringp(lookende) && sizeof(lookende))
+        xl[key]["xlendefun"]=lookende;
+  }
+  else {
+    // Einfacher Eintrag, nur den bearbeiteten String merken. ;-)
+    xl[key]=(["xllook": break_string(replace_personal(look,({ME}),1),78,
+                                     "",BS_LEAVE_MY_LFS),
+             ]);
+    // ggf. Meldung speichern, die bei Ablauf ausgegeben werden soll.
+    if (stringp(lookende) && sizeof(lookende)) {
+      xl[key]["xlende"]=break_string(replace_personal(lookende,({ME}),1),78,
+                                     "",BS_LEAVE_MY_LFS);
+    }
+  }
+  // Endezeit vermerken.
+  if (duration != 0)
+    xl[key]["xlduration"]=duration;
+
+  // Kein Set noetig, weil Query das Mapping ja als Referenz lieferte.
+  return(1);
+}
+
+public int RemoveExtraLook(string key) {
+  mapping xl;
+  if (!stringp(key) || !sizeof(key)) {
+    // Automatisch erzeugen, wenn moeglich
+    if (!objectp(previous_object()) ||
+        !stringp(key=object_name(previous_object())) || !sizeof(key))
+      return(-1);
+  }
+  xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE); // dran denken: liefert referenz zurueck
+  if (!mappingp(xl))
+    return (-2);
+  if (!member(xl,key))
+    return(-2);
+
+  m_delete(xl,key);
+  // Kein Set noetig, weil Query das Mapping ja als Referenz lieferte.
+  return(1);
+}
+
+void create()
+{ 
+  ::create();
+  Set(P_GENDER, SAVE, F_MODE);
+  // Extralook-Property speichern und vor manueller Aenderung schuetzen
+  // EMs duerfen, die wissen hoffentlich, was sie tun.
+  Set(P_INTERNAL_EXTRA_LOOK, SAVE|PROTECTED, F_MODE_AS);
+  SetProp(P_CLOTHING,({}));
+  AddId("Living");
+}
+
+string condition()
+{
+  int hpnt, max_hpnt, perc;
+
+  hpnt        = QueryProp( P_HP );
+  max_hpnt    = QueryProp( P_MAX_HP );
+
+  if(max_hpnt>0 && hpnt>0)
+    perc=100*hpnt/max_hpnt;
+ 
+  switch(perc) {
+    case 0..9:
+        return capitalize(QueryPronoun(WER))+" steht auf der Schwelle des Todes.\n";
+    case 10..19:
+        return capitalize(QueryPronoun(WER))+" braucht dringend einen Arzt.\n";
+    case 20..29:
+        return capitalize(QueryPronoun(WER))+" ist in keiner guten Verfassung.\n";
+    case 30..39:
+        return capitalize(QueryPronoun(WER))+" wankt bereits bedenklich.\n";
+    case 40..49:
+        return capitalize(QueryPronoun(WER))+" macht einen mitgenommenen Eindruck.\n";
+    case 50..59:
+        return capitalize(QueryPronoun(WER))+" sieht nicht mehr taufrisch aus.\n";
+    case 60..69:
+        return capitalize(QueryPronoun(WER))+" ist leicht angeschlagen.\n";
+    case 70..79:
+        return capitalize(QueryPronoun(WER))+" fuehlte sich heute schon besser.\n";
+    case 80..89:
+        return capitalize(QueryPronoun(WER))+" ist schon etwas geschwaecht.\n";
+  }
+  //fall-through
+  return capitalize(QueryPronoun(WER))+" ist absolut fit.\n";
+}
+
+varargs string long() {
+  string str, cap_pronoun;
+  string descr, invl,tmp,exl;
+  int hpnt, max_hpnt;
+  mixed filter_ldfied;
+  object ob;
+
+  str = process_string( QueryProp(P_LONG) );
+  if(!stringp(str)) str = "";
+
+  str += condition();
+
+  // Extralook
+  if(stringp(tmp = QueryProp(P_EXTRA_LOOK)))
+    str += tmp;
+  if (stringp(tmp = QueryProp(P_INTERNAL_EXTRA_LOOK)))
+    str += tmp;
+  for(ob = first_inventory(ME); ob; ob = next_inventory(ob))
+    if(exl = ob->QueryProp(P_EXTRA_LOOK)) 
+      str += exl;
+    else if(exl = ob->extra_look()) 
+      str += exl; // TO BE REMOVED
+
+  
+  if(filter_ldfied = QueryProp(P_TRANSPARENT))
+  {
+    invl = make_invlist(PL, all_inventory(ME));
+    if(invl != "")
+      str += capitalize(QueryPronoun(WER))+" traegt bei sich:\n" + invl;
+  }
+  return str;
+}
+
+varargs string name(int casus, int demonst)
+{ 
+  if( QueryProp( P_INVIS ) )
+  {
+    if( casus == RAW ) return "Jemand";
+    return ({"Jemand","Jemands","Jemandem","Jemanden"})[casus];
+  }
+  if (QueryProp(P_FROG) && casus != RAW )
+  {
+    string s=QueryArticle(casus, 0, 1)+"Frosch";
+    if (casus==WESSEN) s += "es";
+    return s;
+  }
+  return ::name( casus, demonst );
+}
+
+static int _query_gender()
+{
+  if (QueryProp(P_FROG)) return 1;
+  return Query(P_GENDER);
+}
+
+// NPC sollen aus Kompatibilitaetsgruenden auch eine "echte" Rasse haben.
+// Default ist hier die Rasse, man kann aber hiermit auch einen NPC faken,
+// der sich tarnt, indem man P_REAL_RACE per Hand setzt.
+static string _query_real_race()
+{
+  return Query(P_REAL_RACE,F_VALUE)||QueryProp(P_RACE);
+}
+
+static mixed _set_name(mixed nm )
+{
+  string lvnam;
+  lvnam = nm;
+  if(pointerp(nm)) lvnam = nm[0];
+  set_living_name(lower_case(lvnam));
+  return Set(P_NAME, nm);
+}
+
+int _query_container()
+{
+  return 0;
+}
+
+int is_class_member(mixed str) {
+  // Keine Klasse, keine Mitgliedschaft ...
+  if (!str || (!stringp(str) && !pointerp(str)) || str=="") 
+      return 0;
+
+  if (::is_class_member(str))
+    return 1;
+
+  if (stringp(str))
+    str = ({str});
+
+  // Rassen werden als implizite Klassen aufgefasst.
+  // TODO: Pruefen, ob das unbedingt hart-kodiert sein muss.
+  string race = QueryProp(P_RACE);
+  if ( stringp(race) && member( str, lower_case(race) ) > -1 )
+    return 1;
+  else
+    return 0;
+}
+
+mapping _query_material() {
+  mixed res;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  return ([MAT_MISC_LIVING:100]);
+}
+
diff --git a/std/living/helpers.c b/std/living/helpers.c
new file mode 100644
index 0000000..3d292c0
--- /dev/null
+++ b/std/living/helpers.c
@@ -0,0 +1,149 @@
+// MorgenGrauen MUDlib
+//
+// living/helpers.c -- (Query)Methoden fuer Hilfsobjekte, z.B. zum Tauchen
+//
+// $Id: moneyhandler.h,v 3.1 1997/02/12 13:29:09 Wargon Exp %
+
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <living/helpers.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+public int RegisterHelperObject(object helper, int type, 
+                                string|closure callback) 
+{
+  // cb: closure auf die Callback-Funktion in previous_object()
+  closure cb;
+  // helpers: Mapping aller eingetragenen Helfer-Objekte
+  mapping helpers;
+
+  // Kein positiver Integerwert als Helfertyp uebergeben?
+  if ( !intp(type) || type < 1 )
+     raise_error(sprintf( "Wrong argument 1 to RegisterHelperObject(). "
+        "Expected positive <int>, got %O.\n", type));
+  // Kein Objekt vorhanden, an dem die Callback-Funktion gerufen werden soll?
+  if ( !objectp(helper) )
+    return HELPER_NO_CALLBACK_OBJECT;
+
+  // Funktionsname zum Zweck des Callbacks uebergeben?
+  if ( stringp(callback) ) {
+    // Dann Closure davon erstellen.
+    cb = symbol_function(callback, helper);
+    // Wenn das nicht klappt (zB weil die Funktion private ist), dann
+    // Fehler werfen und abbrechen.
+    if ( !closurep(cb) )
+      raise_error(sprintf("Error in RegisterHelperObject(): Unable to call "
+        "function %s in object %O.\n", callback, helper));
+  }
+  // Wenn schon eine Closure uebergeben wurde, dann diese direkt speichern.
+  else if ( closurep(callback) ) {
+    cb = callback;
+  }
+  // Weder Funktionsname, noch Closure, dann Fehler werfen und abbrechen.
+  else
+    raise_error(sprintf("Wrong argument 2 to RegisterHelperObject(). "
+      "Expected <string/closure>, got %O.\n",callback));
+
+  // Property auslesen und zwischenspeichern
+  helpers = QueryProp(P_HELPER_OBJECTS);
+  // Wenn die Prop leer ist, hier initialisieren
+  if ( !helpers ) {
+    helpers = ([type:({})]);
+  }
+  // Wenn der Typ noch nicht existiert, hier nachtragen.
+  else if ( !pointerp(helpers[type]) ) {
+    helpers[type] = ({});
+  }
+
+  // Closure eintragen, wenn noch nicht vorhanden
+  if ( member(helpers[type], cb)==-1 ) {
+    helpers[type] = helpers[type]+({cb});
+    SetProp(P_HELPER_OBJECTS, helpers);
+    return HELPER_SUCCESS;
+  }
+  else
+    return HELPER_ALREADY_LISTED;
+}
+
+public int UnregisterHelperObject(object helper, int type) {
+  if ( !intp(type) || type < 1 )
+     raise_error(sprintf( "Wrong argument 2 to UnregisterHelperObject(). "
+        "Expected positive <int>, got %O.\n", type));
+  if ( !objectp(helper) )
+    return HELPER_NO_CALLBACK_OBJECT;
+
+  mapping helpers = Query(P_HELPER_OBJECTS, F_VALUE);
+
+  if ( mappingp(helpers) ) {
+    foreach(closure cl: helpers[type]) {
+      if ( get_type_info(cl,2) == helper ) {
+        helpers[type] = helpers[type]-({cl});
+        return HELPER_SUCCESS;
+      }
+    }
+  }
+  return HELPER_NOTHING_TO_UNREGISTER;
+}
+
+// Querymethode fuer P_AQUATIC_HELPERS
+public mapping _query_lib_p_aquatic_helpers() {
+  mapping ret = ([]);
+  // eingetragene Callback-Closures auslesen
+  closure *helpers = 
+    ( Query(P_HELPER_OBJECTS, F_VALUE) || ([]) )[HELPER_TYPE_AQUATIC];
+  // Es sind gar keine Werte eingetragen? Dann gleich rausspringen.
+  if ( !pointerp(helpers) )
+    return ret;
+
+  // Nullelement substrahieren
+  helpers -= ({0});
+
+  if ( sizeof(helpers) ) {
+    // Mapping erstellen: Keys sind die Objekte, deren Closures eingetragen
+    // sind. Values sind die Rueckgabewerte der Closures,
+    // die dabei das Spielerobjekt und das abfragende Objekt uebergeben
+    // bekommen.
+    object *keys = map(helpers, #'get_type_info, 2);
+    int *vals = map(helpers, #'funcall, this_object(), previous_object(1));
+    ret = mkmapping(keys,vals);
+  }
+  return ret;
+}
+
+// Querymethode fuer P_AERIAL_HELPERS
+public mapping _query_lib_p_aerial_helpers() {
+  mapping ret = ([]);
+  // eingetragene Callback-Closures auslesen
+  closure *helpers = 
+    ( Query(P_HELPER_OBJECTS, F_VALUE) || ([]) )[HELPER_TYPE_AERIAL];
+
+  // Es sind gar keine Werte eingetragen? Dann gleich rausspringen.
+  if ( !pointerp(helpers) )
+    return ret;
+  
+  // Nullelement substrahieren
+  helpers -= ({0});
+
+  if ( sizeof(helpers) ) {
+    // Mapping erstellen: Keys sind die Objekte, deren Closures eingetragen
+    // sind. Values sind die Rueckgabewerte der Closures,
+    // die dabei das Spielerobjekt und das abfragende Objekt uebergeben
+    // bekommen.
+    object *keys = map(helpers,#'get_type_info, 2);
+    int *vals = map(helpers, #'funcall, this_object(), previous_object(1));
+    ret = mkmapping(keys,vals);
+  }
+  return ret;
+}
+
+// Querymethode fuer P_HELPER_OBJECTS
+public mapping _query_lib_p_helper_objects() {
+  return deep_copy(Query(P_HELPER_OBJECTS,F_VALUE));
+}
+
diff --git a/std/living/inventory.c b/std/living/inventory.c
new file mode 100644
index 0000000..e3027e4
--- /dev/null
+++ b/std/living/inventory.c
@@ -0,0 +1,66 @@
+// MorgenGrauen MUDlib
+//
+// container/inventory.c -- Umgang mit besonderen Objekten im Inventory
+//
+// $Id: inventory.c 6554 2007-10-17 22:45:53Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/inventory";
+
+#define NEED_PROTOTYPES
+
+#include <properties.h>
+#include <sensitive.h>
+#include <attributes.h>
+
+#define ME this_object()
+
+public void RemoveSensitiveObject(object ob) {
+  ::RemoveSensitiveObject(ob);
+  RemoveSensitiveObjectFromList(ob,SENSITIVE_ATTACK);
+  if (objectp(ob) && (ob->QueryProp(P_X_ATTR_MOD)   ||
+                      ob->QueryProp(P_X_HEALTH_MOD) ))
+  {
+    deregister_modifier(ob);
+    // Erst wenn das Objekt den Spieler verlassen konnte, die Attribute
+    // neu berechnen.
+    if (find_call_out("UpdateAttributes")==-1)
+      call_out("UpdateAttributes",0); 
+  }
+}
+
+public void InsertSensitiveObject(object ob, mixed arg) {
+  ::InsertSensitiveObject(ob,arg);
+  if (objectp(ob) && (ob->QueryProp(P_X_ATTR_MOD)   ||
+                      ob->QueryProp(P_X_HEALTH_MOD) ))
+  {
+    register_modifier(ob);
+    UpdateAttributes();
+  }
+}
+
+public void CheckSensitiveAttack(int dam, mixed dam_type, mixed spell, 
+                                 object enemy) {
+  mixed a,x;
+  int i;
+  
+  if (!pointerp(a=QueryProp(P_SENSITIVE_ATTACK)))
+    return;
+  if (!pointerp(dam_type))
+    dam_type=({dam_type});
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (pointerp(x=a[i]) &&
+	dam>x[SENS_THRESHOLD] &&
+	member(dam_type,x[SENS_KEY])>=0 &&
+	objectp(x[SENS_OBJECT]) &&
+	environment(x[SENS_OBJECT])==ME &&
+	closurep(x[SENS_CLOSURE]))
+      funcall(x[SENS_CLOSURE],
+	      enemy,x[SENS_KEY],dam,
+	      spell,x[SENS_OPT]);
+}
+
diff --git a/std/living/life.c b/std/living/life.c
new file mode 100644
index 0000000..f3a72f5
--- /dev/null
+++ b/std/living/life.c
@@ -0,0 +1,1572 @@
+// MorgenGrauen MUDlib
+//
+// living/life.c -- life variables
+//
+// $Id: life.c 9426 2016-01-03 10:02:57Z Zesstra $
+
+// living object life variables
+//
+//  P_ALIGN         -- alignment value
+//  P_NPC           -- if living is an NPC
+//  P_HP            -- HitPoints
+//  P_SP            -- SpellPoints
+//  P_ALCOHOL       -- value of intoxication
+//  P_DRINK         -- value of soakness
+//  P_FOOD          -- value of stuffness
+//  P_XP            -- experience
+//  P_POISON        -- level of poison
+//  P_CORPSE        -- corpse-object
+//  P_DEAF          -- if living is deaf
+#pragma strong_types,save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#include <living/skills.h>
+#include <thing/properties.h>
+#include <living/life.h>
+#include <living/moving.h>
+#include <living/combat.h>
+#include <living/attributes.h>
+#include <thing/description.h>
+#include <thing/language.h>
+#undef NEED_PROTOTYPES
+#include <health.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <scoremaster.h>
+#include <defuel.h>
+#include <properties.h>
+#include <events.h>
+#include <wizlevels.h>
+
+#define ALCOHOL_VALUE(n) n
+
+#include <debug_info.h> //voruebergehend
+
+
+// 'private'-Prototypen
+private void DistributeExp(object enemy, int exp_to_give);
+
+// Variablen
+nosave int delay_alcohol; /* time until next alcohol effect */
+nosave int delay_drink;   /* time until next drink effect */
+nosave int delay_food;    /* time until next food effect */
+nosave int delay_heal;    /* time until next heal effect */
+nosave int delay_sp;      /* time until next sp regeneration */
+nosave int delay_poison;  /* time until next poison effect */
+nosave int drop_poison;
+nosave mapping enemy_damage;
+nosave mapping hp_buffer;
+nosave mapping sp_buffer;
+nosave int remove_me;
+int nextdefueltimefood;
+int nextdefueltimedrink;
+
+
+protected void create()
+{
+  Set(P_GHOST, SAVE, F_MODE);
+  Set(P_FROG, SAVE, F_MODE);
+  Set(P_ALIGN, SAVE, F_MODE);
+  Set(P_HP, SAVE, F_MODE);
+  Set(P_SP, SAVE, F_MODE);
+  Set(P_XP, SAVE, F_MODE);
+  Set( P_LAST_XP, ({ "", 0 }) );
+  Set( P_LAST_XP, PROTECTED, F_MODE_AS );
+
+  Set(P_ALCOHOL, SAVE, F_MODE);
+  Set(P_DRINK, SAVE, F_MODE);
+  Set(P_FOOD, SAVE, F_MODE);
+  Set(P_POISON, SAVE, F_MODE);
+  Set(P_DEAF, SAVE, F_MODE);
+
+  SetProp(P_FOOD_DELAY, FOOD_DELAY);
+  SetProp(P_DRINK_DELAY, DRINK_DELAY);
+  SetProp(P_ALCOHOL_DELAY, ALCOHOL_DELAY);
+  SetProp(P_HP_DELAY,HEAL_DELAY);
+  SetProp(P_SP_DELAY,HEAL_DELAY);
+  SetProp(P_POISON_DELAY,POISON_DELAY);
+  // default fuer alle Lebewesen (NPC + Spieler):
+  SetProp(P_MAX_POISON, 10);
+  SetProp(P_CORPSE, "/std/corpse");
+
+  nextdefueltimefood=time()+QueryProp(P_DEFUEL_TIME_FOOD);
+  nextdefueltimedrink=time()+QueryProp(P_DEFUEL_TIME_DRINK);
+
+  enemy_damage=([:2 ]);
+  hp_buffer=([]);
+  sp_buffer=([]);
+
+  SetProp(P_DEFUEL_LIMIT_FOOD,1);
+  SetProp(P_DEFUEL_LIMIT_DRINK,1);
+  SetProp(P_DEFUEL_TIME_FOOD,1);
+  SetProp(P_DEFUEL_TIME_DRINK,1);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,1);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,1);
+
+  offerHook(H_HOOK_DIE,1);
+
+  offerHook(H_HOOK_FOOD,1);
+  offerHook(H_HOOK_DRINK,1);
+  offerHook(H_HOOK_ALCOHOL,1);
+  offerHook(H_HOOK_POISON,1);
+  offerHook(H_HOOK_CONSUME,1);
+}
+
+// Wenn der letzte Kampf lang her ist und das Lebewesen wieder vollgeheilt
+// ist, wird P_ENEMY_DAMAGE zurueckgesetzt.
+protected void ResetEnemyDamage() {
+  if (time() > QueryProp(P_LAST_COMBAT_TIME) + __RESET_TIME__ * 4
+      && QueryProp(P_HP) == QueryProp(P_MAX_HP))
+    enemy_damage=([:2 ]);
+}
+
+private void DistributeExp(object enemy, int exp_to_give) {
+  int total_damage, tmp, ex;
+  mapping present_enemies;
+
+  if ( exp_to_give<=0 )
+    return;
+
+  mapping endmg=deep_copy(enemy_damage);
+
+  // Mitglieder im Team des Killers bekommen:
+  //
+  //                  Gesamtanteil des Teams
+  // Eigenen Anteil + ----------------------
+  //                  Anzahl  Teammitglieder
+  // ---------------------------------------
+  //                2
+  //
+  object *inv = enemy->TeamMembers();
+  if ( pointerp(inv) )
+  {
+    present_enemies=m_allocate(sizeof(inv), 1);
+    foreach(object ob: inv)
+    {
+      if ( objectp(ob) && (environment(ob)==environment()) )
+      {
+        tmp=endmg[object_name(ob)];
+        total_damage+=tmp;
+        present_enemies[ob] = tmp/2;
+        m_delete(endmg,object_name(ob)); //s.u.
+      }
+    }
+    int mitglieder = sizeof(present_enemies);
+    if ( mitglieder )
+    {
+      tmp=total_damage/(2*mitglieder);
+      foreach(object ob, int punkte: &present_enemies)
+        punkte += tmp;
+    }
+  }
+  else {
+    // ohne Team wird trotzdem ein Mapping gebraucht. Da Groessenveraenderung
+    // rel. teuer sind, kann einfach mal fuer 3 Eintraege Platz reservieren.
+    present_enemies=m_allocate(3, 1);
+  }
+  // Und noch die Lebewesen im Raum ohne Team.
+  foreach(object ob: all_inventory(environment()))
+  {
+    if ( tmp=endmg[object_name(ob)] )
+    {
+      total_damage += tmp;
+      present_enemies[ob] = tmp;
+      m_delete(endmg,object_name(ob)); // Nur einmal pro Leben Punkte :)
+    }
+  }
+  if ( !total_damage )
+  {
+    enemy->AddExp(exp_to_give);
+  }
+  else
+  {
+    foreach(object ob, int damage: present_enemies)
+    {
+      if ( !objectp(ob) )
+        continue;
+      if ( query_once_interactive(ob) && ( !interactive(ob)
+              || (query_idle(ob)>600) ) )
+        continue;
+      //exp_to_give*present_enemies[i][1]/total_damage gibt bei viel Schaden
+      //einen numerical overflow. Daher muessen wir hier wohl doch
+      //zwischenzeitlich mit floats rechnen, auch wenn das 0-1 XP Verlust
+      //durch float->int-Konversion gibt. (ceil() lohnt sich IMHO nicht.)
+      ex = (int)(exp_to_give*((float)damage/(float)total_damage));
+      ob->AddExp(ex);
+    }
+  }
+}
+
+/*
+ * This function is called from other players when they want to make
+ * damage to us. But it will only be called indirectly.
+ * We return how much damage we received, which will
+ * change the attackers score. This routine is probably called from
+ * heart_beat() from another player.
+ * Compare this function to reduce_hit_points(dam).
+ */
+public int do_damage(int dam, object enemy)
+{ int hit_point,al,al2;
+
+  if ( extern_call()
+      && objectp(enemy)
+      && living(enemy)
+      && !QueryProp(P_ENABLE_IN_ATTACK_OUT))
+  {
+    al=time()-enemy->QueryProp(P_LAST_MOVE);
+    if (al<3)      // Erste Kampfrunde nach Betreten des Raumes?
+      dam/=(4-al); // Gegen Rein-Feuerball-Raus-Taktik
+  }
+
+  if ( QueryProp(P_GHOST) || QueryProp(P_NO_ATTACK) || (dam<=0)
+      || ( objectp(enemy)
+          && ( enemy->QueryProp(P_GHOST)
+              || enemy->QueryProp(P_NO_ATTACK) ) ) )
+    return 0;
+
+  hit_point = QueryProp(P_HP)-dam;
+
+  if ( QueryProp(P_XP) && objectp(enemy) )
+  {
+    if ( !QueryProp(P_NO_XP) )
+      enemy->AddExp(dam*(int)QueryProp(P_TOTAL_WC)/10);
+  }
+
+  if (living(enemy)) {
+      string enname = object_name(enemy);
+      // Hmpf. Blueprints sind doof. Die Chance ist zwar gering, aber koennte
+      // sein, dass ein Unique-NPC mit zwei verschiedenen Spielern am gleichen
+      // NPC metzelt.
+      // TODO: MHmm. wie gross ist das Risiko wirklich?
+      //if (!clonep(enemy))
+      //    enname = enname + "_" + to_string(object_time(enemy));
+      // nur wenn gegner NPC ist und noch nicht drinsteht: Daten aus
+      // P_HELPER_NPC auswerten
+      if (!member(enemy_damage,enemy) && !query_once_interactive(enemy)) {
+          mixed helper = enemy->QueryProp(P_HELPER_NPC);
+          if (pointerp(helper) && objectp(helper[0]))
+              enemy_damage[enname,1] = helper[0];
+      }
+      enemy_damage[enname,0]+=dam;
+  }
+
+  SetProp(P_HP, hit_point);
+
+  if ( hit_point<0 )
+  {
+    //TODO: Warum nicht das ganze Zeug ins die() verlegen?
+    if ( enemy )
+    {
+      enemy->StopHuntFor(ME,1);
+      if ( !QueryProp(P_NO_XP) )
+        DistributeExp(enemy,QueryProp(P_XP)/100);
+      if ( !query_once_interactive(ME) )
+        log_file ("NPC_XP", sprintf(
+	    "[%s] %s, XP: %d, HP*WC: %d, Killer: %s\n",
+	    dtime(time()), object_name(ME), (QueryProp(P_XP)/100),
+                  QueryProp(P_TOTAL_WC)*QueryProp(P_MAX_HP)/10,
+                  enemy->name()||"NoName" ));
+      al = QueryProp(P_ALIGN)/50 + enemy->QueryProp(P_ALIGN)/200;
+      if (al>20)
+        al=20;
+      else if(al<-20)
+        al=-20;
+      enemy->SetProp(P_ALIGN,enemy->QueryProp(P_ALIGN)-al);
+    }
+    SetProp(P_KILLER, enemy);
+    
+    die();
+  }
+  return dam;
+}
+
+
+private void _transfer( object *obs, string|object dest, int flag )
+{   int i;
+
+    i = sizeof(obs);
+
+    // Eine Schleife ist zwar langsamer als filter() o.ae., aber
+    // selbst mit einer noch so schnellen Loesung kann leider nicht
+    // ausgeschlossen werden, dass irgendwo ein too-long-eval-Bug dazwischen
+    // kommt. Dazu sind die Kaempfe mit Gilden-NPCs etc. einfach zu teuer ...
+    // Pruefung auf zerstoerte Objekte, da einige sich evtl. im NotifyPlayerDeath() 
+    // zerstoeren.
+   while ( i && get_eval_cost() > 300000 )
+        if ( objectp(obs[--i]) && !obs[i]->QueryProp(P_NEVERDROP) )
+        // Jetzt wird's noch etwas teurer mit catch() - aber manche Sachen
+        // duerfen einfach nicht buggen
+            catch( obs[i]->move( dest, flag );publish );
+
+    if ( i > 0 )
+        // Zuviel Rechenzeit verbraten, es muessen noch Objekte bewegt werden
+        call_out( #'_transfer, 0, obs[0..i-1], dest, flag );
+    else {
+        if ( remove_me )
+            remove();
+    }
+}
+
+
+public varargs void transfer_all_to( string|object dest, int isnpc ) {
+    int flags;
+    object *obs;
+
+    if ( !objectp(ME) )
+        return;
+
+    // Das Flag "isnpc" ist fuer NPCs gedacht. Deren Ausruestung darf nicht
+    // mit M_NOCHECK bewegt werden, da Spieler das bei Nicht-Standard-Leichen
+    // sonst u.U. ausnutzen koennten.
+    if ( isnpc )
+        flags = M_SILENT;
+    else
+        flags = M_SILENT|M_NOCHECK;
+
+    obs = all_inventory(ME) || ({});
+
+    // unnoetig, weil _transfer() auch auf P_NEVERDROP prueft. Zesstra
+    //obs -= filter_objects( obs, "QueryProp", P_NEVERDROP );
+
+    _transfer( obs, dest, flags );
+}
+
+
+protected varargs void create_kill_log_entry(string killer, object enemy) {
+  int level,lost_exp;
+
+  if ( (level=QueryProp(P_LEVEL))<20 || !IS_SEER(ME) )
+    lost_exp = QueryProp(P_XP)/3;
+  else
+    lost_exp = QueryProp(P_XP)/(level-17);
+  
+  log_file("KILLS",sprintf("%s %s (%d,%d) %s\n", strftime("%e %b %H:%M"),
+               capitalize(REAL_UID(ME)), level, lost_exp/1000, killer)); 
+}
+
+// Liefert im Tod (nach dem toetenden do_damage()) das Spielerobjekt, was den
+// Tod wohl zu verantworten hat, falls es ermittelt werden kann. Es werden vor
+// allem registrierte Helfer-NPC und einige Sonderobjekte beruecksichtigt.
+protected object get_killing_player()
+{
+  object killer=QueryProp(P_KILLER);
+  // koennte sein, wenn ausserhalb des Todes gerufen oder eine Vergiftung uns
+  // umgebracht hat.
+  if (!objectp(killer))
+    return 0;
+
+  while (killer && !query_once_interactive(killer))
+    killer = killer->QueryUser();
+
+  return killer;
+}
+
+protected object GiveKillScore(object pl, int npcnum)
+{
+  // Stufenpunkt fuer den Kill vergeben.
+  // Falls der Killer den Punkt schon hat, wird
+  // zufaellig ein Mitglied seines Teams ausgewaehlt
+  // und diesem der Punkt gegeben.
+  object *obs,ob;
+  mixed *fr;
+  int i,j,sz;
+
+  if ( pointerp(obs=pl->TeamMembers()) && (member(obs,pl)>=0) )
+  {
+    if ( !pointerp(fr=pl->PresentTeamRows())
+        || !sizeof(fr)
+        || !pointerp(fr=fr[0])) // Erste Reihe des Teams
+      fr=({});
+    fr-=({pl,0});
+    obs-=({pl,0});
+    obs-=fr;
+    i=sz=sizeof(obs); // restliche Teammitglieder in zufaelliger Reihenfolge:
+    for ( --i ; i>=0 ; i-- )
+    {
+      j=random(sz);
+      ob=obs[j];
+      obs[j]=obs[0];
+      obs[0]=ob;
+    }
+    i=sz=sizeof(fr);  // Erste Reihe in zufaelliger Reihenfolge:
+    for ( --i ; i>=0 ; i-- )
+    {
+      j=random(sz);
+      ob=fr[j];
+      fr[j]=fr[0];
+      fr[0]=ob;
+    }
+
+    obs+=fr;     // Erste Reihe wird vor Rest getestet
+    obs+=({pl}); // Killer wird als erstes getestet
+  }
+  else
+  {
+    obs=({pl});
+  }
+  for ( i=sizeof(obs)-1 ; i>=0 ; i-- )
+    if ( objectp(ob=obs[i] )
+        && interactive(ob)     // Nur netztot dabei stehen gilt nicht :)
+        && query_idle(ob)<600  // gegen Leute die sich nur mitschleppen lassen
+        && environment(ob)==environment(pl) // Nur anwesende Teammitglieder
+        && !IS_LEARNER(ob)
+//        && !ob->QueryProp(P_TESTPLAYER)
+        && !(SCOREMASTER->HasKill(ob,ME)) )
+      return SCOREMASTER->GiveKill(ob,npcnum),ob;
+
+  return SCOREMASTER->GiveKill(pl,npcnum),pl;
+}
+
+// zum ueberschreiben in Spielern
+public int death_suffering() {
+  return 0; // NPC haben keine Todesfolgen
+}
+
+// kein 2. Leben fuer Nicht-Spieler. ;-)
+varargs protected int second_life( object corpse ) {
+    return 0;
+}
+
+public varargs void die( int poisondeath, int extern )
+{   object corpse;
+    string die_msg, tmp;
+    mixed res;
+    mixed hookData;
+    mixed hookRes;
+
+    if ( !objectp(this_object()) || QueryProp(P_GHOST) )
+        return; // Ghosts dont die ...
+
+    // direkt von extern aufgerufen und nicht ueber heart_beat() oder
+    // do_damage() hierher gelangt?
+    if (extern_call() && previous_object() != this_object()) {
+      extern=1;
+      SetProp(P_KILLER, previous_object());
+    }
+
+    if ( res = QueryProp(P_TMP_DIE_HOOK) ){
+        if ( pointerp(res) && sizeof(res)>=3
+            && intp(res[0]) && time()<res[0]
+            && objectp(res[1]) && stringp(res[2]) )
+        {
+            if ( res = call_other( res[1], res[2], poisondeath ) ) {
+              SetProp(P_KILLER,0);
+              return;
+            }
+        }
+        else
+            SetProp(P_TMP_DIE_HOOK,0);
+    }
+
+    // trigger die hook
+    hookData=poisondeath;
+    hookRes=HookFlow(H_HOOK_DIE,hookData);
+    if (pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+      if(hookRes[H_RETCODE]==H_CANCELLED) {
+        SetProp(P_KILLER,0);
+        return;
+      }
+      else if (hookRes[H_RETCODE]==H_ALTERED)
+          poisondeath = hookRes[H_RETDATA];
+    }
+
+    if ( IS_LEARNING(ME) && query_once_interactive(ME) ){
+       tell_object( ME, "Sei froh dass Du unsterblich bist, sonst waere es "
+                        "eben Dein Ende gewesen.\n");
+       SetProp(P_KILLER,0);
+       return;
+    }
+
+    // Gegner befrieden.
+    map_objects( QueryEnemies()[0], "StopHuntFor", ME, 1 );
+    StopHuntingMode(1);
+
+    // Falls die() direkt aufgerufen wurde und dies ein Spieler ist, muss das
+    // die() noch Eintraege in /log/KILLS via create_kill_log_entry bzw. in
+    // /log/KILLER erstellen.
+    if ( query_once_interactive(ME) && extern )
+    {
+      object killer = QueryProp(P_KILLER) 
+                     || previous_object() || this_interactive() || this_player();
+      if ( killer && !query_once_interactive(killer) )
+      {
+          tmp = explode( object_name(killer), "#")[0] + " (direkt !)";
+
+          create_kill_log_entry( tmp + " (" + REAL_UID(killer) + ")", killer );
+      }
+      else if ( killer && !QueryProp(P_TESTPLAYER) && !IS_LEARNER(ME) )
+      {
+          log_file( "KILLER", sprintf( "%s %s (%d/%d) toetete %s (%d/%d)\n",
+                                       ctime(time()),
+                                       capitalize(getuid(killer)),
+                                       query_wiz_level(killer),
+                                       killer->QueryProp(P_LEVEL),
+                                       capitalize(getuid(ME)),
+                                       query_wiz_level(ME),
+                                       QueryProp(P_LEVEL) ) );
+
+          killer->SetProp( P_KILLS, -1 );
+      }
+    }
+
+  // Bei NPC EKs vergeben und ggf. in der Gilde des Killers und im Raum
+  // NPC_Killed_By() rufen.
+  if ( !query_once_interactive(ME) )
+  {
+    object killer = ((object) QueryProp(P_KILLER)) || previous_object() ||
+      this_interactive() || this_player();
+
+    if ( killer && query_once_interactive(killer) )
+    {
+      if (stringp(res=killer->QueryProp(P_GUILD))
+          && objectp(res=find_object("/gilden/"+res)))
+        res->NPC_Killed_By(killer);
+
+      if (environment())
+          environment()->NPC_Killed_By(killer);
+
+      res = QueryProp(P_XP);
+      res = (res < SCORE_LOW_MARK) ? 0 : ((res > SCORE_HIGH_MARK) ? 2 : 1);
+      if ( !QueryProp(P_NO_SCORE) && !IS_LEARNER(killer) &&
+           // !killer->QueryProp(P_TESTPLAYER) &&
+           pointerp( res = SCOREMASTER->QueryNPC(res)) )
+        GiveKillScore( killer, res[0] );
+    }
+  }
+
+  if( !(die_msg = QueryProp(P_DIE_MSG)) )
+    if (QueryProp(P_PLURAL))
+      die_msg = " fallen tot zu Boden.\n";
+    else
+      die_msg = " faellt tot zu Boden.\n";
+
+  if ( poisondeath )
+  {
+      Set( P_LAST_DAMTYPES, ({ DT_POISON }) );
+      Set( P_LAST_DAMTIME, time() );
+      Set( P_LAST_DAMAGE, 1 );
+      die_msg = " wird von Gift hinweggerafft und kippt um.\n";
+  }
+
+  say( capitalize(name(WER,1)) + die_msg );
+
+  // Wenn keine Leiche, dann Kram ins Env legen.
+  if ( QueryProp(P_NOCORPSE) || !(tmp = QueryProp(P_CORPSE))
+      || catch(corpse = clone_object(tmp);publish) 
+      || !objectp(corpse) )
+  {
+      // Magier oder Testspieler behalten ihre Ausruestung.
+      // Sonst kaemen u.U. Spieler an Magiertools etc. heran
+      if ( !(IS_LEARNER(ME) || (tmp = Query(P_TESTPLAYER)) &&
+             (!stringp(tmp) || IS_LEARNER( lower_case(tmp) ))) )
+          transfer_all_to( environment(), 0 );
+      else
+          // Aber sie ziehen sich aus.
+          filter_objects(QueryProp(P_ARMOURS),"DoUnwear",M_NOCHECK,0);
+  }
+  else
+  // sonst in die Leiche legen.
+  {
+      corpse->Identify(ME);
+      corpse->move( environment(), M_NOCHECK|M_SILENT );
+      // Magier oder Testspieler behalten ihre Ausruestung.
+      // Sonst kaemen u.U. Spieler an Magiertools etc. heran
+      if ( !(IS_LEARNER(ME) || (tmp = Query(P_TESTPLAYER)) &&
+             (!stringp(tmp) || IS_LEARNER( lower_case(tmp) ))) )
+          transfer_all_to( corpse, !query_once_interactive(ME) );
+      else
+          // Aber sie ziehen sich aus.
+          filter_objects(QueryProp(P_ARMOURS),"DoUnwear",M_NOCHECK,0);
+  }
+
+  if ( query_once_interactive(ME) ) {
+      Set( P_DEADS, Query(P_DEADS) + 1 );
+      // Spieler-Tod-event ausloesen
+      EVENTD->TriggerEvent(EVT_LIB_PLAYER_DEATH, ([
+      E_OBJECT: ME, E_PLNAME: getuid(ME),
+      E_ENVIRONMENT: environment(), E_TIME: time(),
+      P_KILLER: QueryProp(P_KILLER),
+      P_LAST_DAMAGE: QueryProp(P_LAST_DAMAGE),
+      P_LAST_DAMTYPES: copy(QueryProp(P_LAST_DAMTYPES)),
+      E_EXTERNAL_DEATH: extern,
+      E_POISON_DEATH: poisondeath, 
+      E_CORPSE: (objectp(corpse)?corpse:0) ]) );
+  }
+  else {
+      // NPC-Todes-Event ausloesen. Div. Mappings/Arrays werden nicht kopiert,
+      // weil der NPC ja jetzt eh zerstoert wird.
+      mapping data = ([
+            E_OBNAME: object_name(ME),
+            E_ENVIRONMENT: environment(), E_TIME: time(),
+            P_NAME: QueryProp(P_NAME),
+            P_KILLER: QueryProp(P_KILLER),
+            P_ENEMY_DAMAGE: QueryProp(P_ENEMY_DAMAGE),
+            P_LAST_DAMAGE: QueryProp(P_LAST_DAMAGE),
+            P_LAST_DAMTYPES: QueryProp(P_LAST_DAMTYPES),
+            E_EXTERNAL_DEATH: extern,
+            E_POISON_DEATH: poisondeath,
+            E_CORPSE: (objectp(corpse)?corpse:0),
+            P_XP: QueryProp(P_XP),
+            P_ATTRIBUTES: QueryProp(P_ATTRIBUTES),
+            P_MAX_HP: QueryProp(P_MAX_HP),
+            P_HANDS: QueryProp(P_HANDS),
+            P_ALIGN: QueryProp(P_ALIGN),
+            P_RACE: QueryProp(P_RACE),
+            P_CLASS: QueryProp(P_CLASS),
+            ]);
+      EVENTD->TriggerEvent(EVT_LIB_NPC_DEATH(""), data);
+      EVENTD->TriggerEvent(
+          EVT_LIB_NPC_DEATH(load_name(ME)), data);
+  }
+
+  // transfer_all_to() ist evtl. (wenn zuviele Objekte bewegt werden mussten)
+  // noch nicht ganz fertig und wird per call_out() den Rest erledigen.
+  // Sollte die Leiche dann nicht mehr existieren, verbleiben die restlichen
+  // Objekte im Spieler.
+  // Es bleiben aber auf jeden Fall noch rund 300k Eval-Ticks ueber, damit
+  // kein Spieler dank "evalcost too high" ungeschoren davon kommt.
+  if ( !(second_life(corpse)) )
+  {
+      Set( P_GHOST, 1 ); // Fuer korrekte Ausgabe auf Teamkanal.
+
+      if ( find_call_out(#'_transfer) == -1 )
+          // Falls kein call_out() mehr laeuft, sofort destructen ...
+          remove();
+      else
+          // ... ansonsten vormerken
+          remove_me = 1;
+  }
+}
+
+public void heal_self(int h)
+{
+  if ( h<=0 )
+    return;
+  SetProp(P_HP, QueryProp(P_HP)+h);
+  SetProp(P_SP, QueryProp(P_SP)+h);
+}
+
+
+//--------------------------------------------------------------------------
+//
+//   int defuel_food( /* void */ )
+//
+//   Enttankt den Spieler um einen gewissen Essens-Wert.
+//   Sollte nur von Toiletten aufgerufen werden.
+//
+//--------------------------------------------------------------------------
+public int defuel_food()
+{
+  int food;
+
+  food=QueryProp(P_FOOD);
+
+// wenn spieler kein food hat: return 0
+  if ( !food )
+   return NO_DEFUEL;
+
+// wenn spieler unter enttank-grenze: return -1
+  if ( food < QueryProp(P_DEFUEL_LIMIT_FOOD) )
+   return DEFUEL_TOO_LOW;
+
+// wenn letztes enttanken nicht lange genug zurueckliegt: return -2
+  if ( time() < nextdefueltimefood )
+   return DEFUEL_TOO_SOON;
+
+  food=to_int(((food*QueryProp(P_DEFUEL_AMOUNT_FOOD))/2));
+  food+=random(food);
+
+// sicherheitshalber
+  if ( food > QueryProp(P_FOOD) )
+   food=QueryProp(P_FOOD);
+
+  SetProp(P_FOOD,(QueryProp(P_FOOD)-food));
+
+  nextdefueltimefood=time()+QueryProp(P_DEFUEL_TIME_FOOD);
+
+  return food;
+}
+
+
+//--------------------------------------------------------------------------
+//
+//   int defuel_drink( /* void */ )
+//
+//   Enttankt den Spieler um einen gewissen Fluessigkeits-Wert.
+//   Gleichzeitig wird eine gewisse Menge Alkohol reduziert.
+//   Sollte nur von Toiletten aufgerufen werden.
+//
+//--------------------------------------------------------------------------
+public int defuel_drink()
+{
+  int alc, drink;
+
+  drink=QueryProp(P_DRINK);
+
+// wenn spieler kein drink hat: return 0
+  if ( !drink )
+   return NO_DEFUEL;
+
+// wenn spieler unter enttank-grenze: return -1
+  if ( drink < QueryProp(P_DEFUEL_LIMIT_DRINK) )
+   return DEFUEL_TOO_LOW;
+
+// wenn letztes enttanken nicht lange genug zurueckliegt: return -2
+  if ( time() < nextdefueltimedrink )
+   return DEFUEL_TOO_SOON;
+    
+  drink=to_int(((drink*QueryProp(P_DEFUEL_AMOUNT_DRINK))/2));
+  drink+=random(drink);
+
+// sicherheitshalber
+  if ( drink > QueryProp(P_DRINK) )
+   drink=QueryProp(P_DRINK);
+
+  SetProp(P_DRINK,(QueryProp(P_DRINK)-drink));
+
+// jedes fluessige Enttanken macht auch etwas nuechterner :^)
+// bei sehr kleinen Mengen enttankt man keinen Alkohol
+// ansonsten in Abhaengigkeit von enttankter Menge, P_ALCOHOL und P_WEIGHT
+
+  if ( drink > 9 && QueryProp(P_ALCOHOL) > 0 )
+   {
+    alc=(to_int(exp(log(1.1)*(drink)))*
+         to_int(exp(log(0.67)*(QueryProp(P_ALCOHOL)))))/
+         (QueryProp(P_MAX_DRINK)*QueryProp(P_MAX_ALCOHOL))*
+         (to_int(QueryProp(P_WEIGHT)/1000));
+
+     SetProp(P_ALCOHOL,QueryProp(P_ALCOHOL)-(alc+random(alc)));
+   }
+
+  nextdefueltimedrink=time()+QueryProp(P_DEFUEL_TIME_DRINK);
+ 
+  return drink;
+}
+
+
+public void reduce_spell_points(int h)
+{
+  SetProp(P_SP, QueryProp(P_SP)-h);
+}
+
+public void restore_spell_points(int h)
+{
+  SetProp(P_SP, QueryProp(P_SP)+h);
+}
+
+/* Reduce hitpoints. Log who is doing it. */
+public int reduce_hit_points(int dam)
+{ object o;
+  int i;
+
+#ifdef LOG_REDUCE_HP
+  if (this_player()!=ME)
+  {
+    log_file("REDUCE_HP", name()+" by ");
+    if(!this_player()) log_file("REDUCE_HP","?\n");
+    else {
+      log_file("REDUCE_HP",this_player()->name());
+      o=previous_object();
+      if (o)
+        log_file("REDUCE_HP", " " + object_name(o) + ", " +
+                 o->name(WER,0) + " (" + creator(o) + ")\n");
+      else
+        log_file("REDUCE_HP", " ??\n");
+    }
+  }
+#endif
+  if ((i=QueryProp(P_HP)) <= dam)
+    return SetProp(P_HP,1);
+  return SetProp(P_HP, i - dam);
+}
+
+public int restore_hit_points(int heal)
+{
+  return reduce_hit_points(-heal);
+}
+
+public varargs int drink_alcohol(int strength,int testonly, string mytext)
+{ int alc,add,res;
+
+  add=ALCOHOL_VALUE(strength);
+  res=UseSkill(SK_BOOZE,([
+      SI_SKILLARG : add,
+      SI_TESTFLAG : 1])); // Kann der Spieler gut saufen?
+  if (intp(res) && res>0) add=res;
+  alc=QueryProp(P_ALCOHOL)+add;
+  if ((alc >= QueryProp(P_MAX_ALCOHOL)) && !IS_LEARNING(this_object())){
+    if(!testonly)
+      tell_object(ME,mytext||"So ein Pech, Du hast alles verschuettet.\n");
+    return 0;
+  }
+  if(testonly)return 1;
+  UseSkill(SK_BOOZE,([ SI_SKILLARG : ALCOHOL_VALUE(strength) ]));
+  if(alc < 0) alc = 0;
+  if(!alc) tell_object(ME, "Du bist stocknuechtern.\n");
+  SetProp(P_ALCOHOL, alc);
+  return 1;
+}
+
+public varargs int drink_soft(int strength, int testonly, string mytext)
+{ int soaked;
+
+  soaked = QueryProp(P_DRINK);
+  if((soaked + strength > QueryProp(P_MAX_DRINK)) &&
+     !IS_LEARNING(this_object())){
+    if(!testonly)
+     tell_object(ME, mytext||
+       "Nee, so viel kannst Du momentan echt nicht trinken.\n" );
+    return 0;
+  }
+  if(testonly)return 1;
+  if((soaked += DRINK_VALUE(strength)) < 0) soaked = 0;
+  if(!soaked) tell_object(ME, "Dir klebt die Zunge am Gaumen.\n");
+  SetProp(P_DRINK, soaked);
+  return 1;
+}
+
+public varargs int eat_food(int strength, int testonly, string mytext)
+{ int stuffed;
+
+  stuffed = QueryProp(P_FOOD);
+  if ((stuffed + strength > QueryProp(P_MAX_FOOD)) &&
+      !IS_LEARNING(this_object()))
+  {
+    if(!testonly)
+        tell_object(ME,
+            mytext || "Das ist viel zu viel fuer Dich! Wie waers mit etwas "
+            "leichterem?\n");
+    return 0;
+  }
+  if(testonly)return 1;
+  stuffed += FOOD_VALUE(strength);
+  if(stuffed < 0) stuffed = 0;
+  if(!stuffed) tell_object(ME, "Was rumpelt denn da in Deinem Bauch?\n");
+  SetProp(P_FOOD, stuffed);
+  return 1;
+}
+
+public int buffer_hp(int val,int rate)
+{
+  int dif;
+
+  if(val<=0 || rate<=0)return 0;
+  if(val < rate)rate = val;
+  if(rate>20) rate=20;
+
+  /* Check for BufferOverflow */
+  if((dif=(hp_buffer[0]+val)-QueryProp(P_MAX_HP)) > 0)val-=dif;
+  if(val<=0)return 0;
+
+  hp_buffer[0] += val;
+  hp_buffer[1+rate] += val;
+  if(rate > hp_buffer[1])hp_buffer[1] = rate;
+
+  return hp_buffer[0];
+}
+
+public int buffer_sp(int val,int rate)
+{
+  int dif;
+
+  if(val<=0 || rate<=0)return 0;
+  if(val < rate)rate = val;
+  if(rate>20) rate=20;
+
+  /* Check for BufferOverflow */
+  if((dif=(sp_buffer[0]+val)-QueryProp(P_MAX_SP)) > 0)val-=dif;
+  if(val<=0)return 0;
+
+  sp_buffer[0] += val;
+  sp_buffer[1+rate] += val;
+  if(rate > sp_buffer[1])sp_buffer[1] = rate;
+
+  return sp_buffer[0];
+}
+
+protected void update_buffers()
+{ int i, rate, max;
+
+  rate=0;
+  max=0;
+  for(i=1;i<=20;i++){
+    if(member(hp_buffer, i+1))
+      if(hp_buffer[i+1]<=0)
+        hp_buffer = m_delete(hp_buffer,i+1);
+      else{
+        max+=hp_buffer[i+1];
+        rate=i;
+      }
+  }
+
+  hp_buffer[0]=max;
+  hp_buffer[1]=rate;
+  rate=0;
+  max=0;
+  for(i=1;i<=20;i++){
+    if(member(sp_buffer, i+1))
+      if(sp_buffer[i+1]<=0)
+        sp_buffer = m_delete(sp_buffer,i+1);
+      else{
+        max+=sp_buffer[i+1];
+        rate=i;
+      }
+  }
+  sp_buffer[0]=max;
+  sp_buffer[1]=rate;
+}
+
+public int check_timed_key(string key) {
+
+  // keine 0 als key (Typ wird per RTTC geprueft)
+  if (!key)
+    return 0;
+  mapping tmap=Query(P_TIMING_MAP, F_VALUE);
+  if (!mappingp(tmap)) {
+    tmap=([]);
+    Set(P_TIMING_MAP, tmap, F_VALUE);
+  }
+  // Wenn key noch nicht abgelaufen, Ablaufzeitpunkt zurueckgeben.
+  // Sonst 0 (key frei)
+  return (time() < tmap[key]) && tmap[key];
+}
+
+public int check_and_update_timed_key(int duration,string key) {
+
+  // wenn key noch gesperrt, die zeit der naechsten Verfuegbarkeit
+  // zurueckgeben.
+  int res = check_timed_key(key);
+  if (res) {
+    return res;
+  }
+
+  // duration <= 0 ist unsinnig. Aber key ist nicht mehr gesperrt, d.h. time()
+  // ist ein sinnvoller Rueckgabewert.
+  if (duration <= 0)
+    return time();
+ 
+  mapping tmap = Query(P_TIMING_MAP,F_VALUE);
+  tmap[key]=time()+duration;
+  
+  // speichern per SetProp() unnoetig, da man das Mapping direkt aendert,
+  // keine Kopie.
+  //SetProp(P_TIMING_MAP, tmap);
+  
+  return -1; // Erfolg.
+}
+
+protected void expire_timing_map() {
+  
+  mapping tmap = Query(P_TIMING_MAP, F_VALUE);
+  if (!mappingp(tmap) || !sizeof(tmap))
+    return;
+  foreach(string key, int endtime: tmap) {
+    if (endtime < time())
+      m_delete(tmap, key);
+  }
+  // speichern per SetProp() unnoetig, da man das Mapping direkt aendert,
+  // keine Kopie.
+}
+
+protected void heart_beat()
+{
+    if ( !this_object() )
+        return;
+
+    attribute_hb();
+
+    // Als Geist leidet man nicht unter so weltlichen Dingen wie
+    // Alkohol, Gift&Co ...
+    if ( QueryProp(P_GHOST) )
+        return;
+
+    int hpoison = QueryProp(P_POISON);
+    int rlock = QueryProp(P_NO_REGENERATION);
+    int hp = QueryProp(P_HP);
+    int sp = QueryProp(P_SP);
+    int alc;
+
+    // Wenn Alkohol getrunken: Alkoholauswirkungen?
+    if ( (alc = QueryProp(P_ALCOHOL)) && !random(40) ){
+        int n;
+        string gilde;
+        object ob;
+
+        n = random( 5 * (alc - 1)/QueryProp(P_MAX_ALCOHOL) );
+
+        switch (n){
+        case ALC_EFFECT_HICK:
+            say( capitalize(name( WER, 1 )) + " sagt: <Hick>!\n" );
+            write( "<Hick>! Oh, Tschuldigung.\n" );
+            break;
+            
+        case ALC_EFFECT_STUMBLE:
+            say( capitalize(name( WER, 1 )) + " stolpert ueber " +
+                 QueryPossPronoun( FEMALE, WEN ) + " Fuesse.\n" );
+            write( "Du stolperst.\n" );
+            break;
+            
+        case ALC_EFFECT_LOOKDRUNK:
+            say( capitalize(name( WER, 1 )) + " sieht betrunken aus.\n" );
+            write( "Du fuehlst Dich benommen.\n" );
+            break;
+            
+        case ALC_EFFECT_RUELPS:
+            say( capitalize(name( WER, 1 )) + " ruelpst.\n" );
+            write( "Du ruelpst.\n" );
+            break;
+        }
+       
+        // Gilde und Environment informieren ueber Alkoholauswirkung.
+        if ( stringp(gilde = QueryProp(P_GUILD))
+             && objectp(ob = find_object( "/gilden/" + gilde )) )
+            ob->InformAlcoholEffect( ME, n, ALC_EFFECT_AREA_GUILD );
+        
+        if ( environment() )
+            environment()->InformAlcoholEffect( ME, n, ALC_EFFECT_AREA_ENV );
+    }
+    
+    // Alkohol abbauen und etwas extra heilen, falls erlaubt.
+    if ( alc && (--delay_alcohol < 0)
+         && !(rlock & NO_REG_ALCOHOL) ){
+
+        SetProp( P_ALCOHOL, alc - 1 );
+        
+        if ( !hpoison ){
+            hp++;
+            sp++;
+        }
+        
+        delay_alcohol = QueryProp(P_ALCOHOL_DELAY);
+    }
+
+    // P_DRINK reduzieren, falls erlaubt.
+    if ( (--delay_drink < 0) && !(rlock & NO_REG_DRINK) ){
+        delay_drink = QueryProp(P_DRINK_DELAY);
+        SetProp( P_DRINK, QueryProp(P_DRINK) - 1 );
+    }
+
+    // P_FOOD reduzieren, falls erlaubt.
+    if ( (--delay_food < 0) && !(rlock & NO_REG_FOOD) ){
+        delay_food = QueryProp(P_FOOD_DELAY);
+        SetProp( P_FOOD, QueryProp(P_FOOD) - 1 );
+    }
+
+    // Regeneration aus dem HP-Puffer
+    // Hierbei wird zwar nur geheilt, wenn das erlaubt ist, aber der Puffer
+    // muss trotzdem abgearbeitet/reduziert werden, da Delfen sonst eine
+    // mobile Tanke kriegen. (Keine Heilung im Hellen, Puffer bleibt sonst
+    // konstant und kann bei Bedarf ueber dunkelmachende Items abgerufen
+    // werden.)
+    int val;
+    if (hp_buffer[0]) {
+        int rate = hp_buffer[1];
+        val = hp_buffer[rate + 1];
+    
+        if ( val > rate )
+            val = rate;
+        hp_buffer[0] -= val;
+        hp_buffer[rate + 1] -= val;
+        if ( hp_buffer[rate + 1] <= 0 )
+            update_buffers();
+    }
+    // Jetzt Regeneration aus dem Puffer durchfuehren, aber nur wenn erlaubt.
+    if ( val && !(rlock & NO_REG_BUFFER_HP) )
+        hp += val; 
+    // normales Heilen, falls keine Regeneration aus dem Puffer erfolgte und
+    // es erlaubt ist.
+    else if ( (--delay_heal < 0) && !(rlock & NO_REG_HP) ){
+        delay_heal = QueryProp(P_HP_DELAY);
+        if ( !hpoison )
+            hp++;
+    }
+
+    // Gleiches Spiel jetzt fuer den SP-Puffer (s.o.)
+    val=0;
+    if ( sp_buffer[0] ) {
+        int rate = sp_buffer[1];
+        val = sp_buffer[rate + 1];
+        
+        if ( val > rate )
+            val = rate;
+        
+        sp_buffer[0] -= val;
+        sp_buffer[rate + 1] -= val;
+ 
+        if ( sp_buffer[rate + 1] <= 0 )
+            update_buffers();
+    }
+    // Regeneration erlaubt?
+    if ( val && !(rlock & NO_REG_BUFFER_SP) )
+         sp += val;
+    // Wenn nicht, normales Hochideln versuchen.
+    else if ( (--delay_sp < 0) && !(rlock & NO_REG_SP) ){
+        delay_sp = QueryProp(P_SP_DELAY);
+        if ( !hpoison )
+            sp++;
+    }
+
+    if ( hpoison && (interactive(ME) || !query_once_interactive(ME)) ){
+        // Vanion, 26.10.03
+        // Wenn _set_poison() per SET_METHOD ueberschrieben wird, kann
+        // nicht sichergestellt werden, dass poison immer groesser 0 ist
+        // Daher muss hier ein Test rein, so teuer das auch ist :(
+        if (--hpoison < 0)
+          hpoison=0;
+        
+        if ( --delay_poison < 0 ){
+            delay_poison = QueryProp(P_POISON_DELAY)
+                + random(POISON_MERCY_DELAY);
+            hp -= hpoison;
+
+            if ( hp < 0 ){
+                tell_object( ME, "Oh weh - das Gift war zuviel fuer Dich!\n"
+                             + "Du stirbst.\n" );
+                
+                if ( query_once_interactive(ME) ){
+                    create_kill_log_entry( "Vergiftung", 0 );
+                    
+                    // Beim Gifttod gibt es keinen Killer. Aber auf diese Art
+                    // erkennt der Todesraum die Ursache korrekt und gibt die
+                    // richtige Meldung aus.
+                    SetProp( P_KILLER, "gift" );
+                }
+                
+                die(1);
+                return;
+            }
+            
+            if ( (hpoison < 3 || !query_once_interactive(ME) )
+                 && --drop_poison < 0)
+            {
+                // Giftlevel eins reduzieren. hpoison wurde oben schon
+                // reduziert, d.h. einfach hpoison in P_POISON schreiben.
+                // dabei wird dann auch ggf. drop_poison richtig gesetzt.
+                SetProp( P_POISON, hpoison );
+                if ( !hpoison )
+                    tell_object( ME, "Du scheinst die Vergiftung "
+                                     "ueberwunden zu haben.\n" );  
+            }
+        }
+        
+        if ( hpoison && !random(15) )
+            switch ( hp*100/QueryProp(P_MAX_HP) ){
+            case 71..100 :
+                write( "Du fuehlst Dich nicht gut.\n" );
+                say( capitalize(name(WER)) +
+                     " sieht etwas benommen aus.\n" );
+                break;
+                
+            case 46..70 :
+                write( "Dir ist schwindlig und Dein Magen revoltiert.\n" );
+                say( capitalize(name(WER)) + " taumelt ein wenig.\n" );
+                break;
+                
+            case 26..45 :
+                write( "Dir ist heiss. Du fuehlst Dich schwach. Kopfweh "
+                       "hast Du auch.\n" );
+                say( capitalize(name(WER)) + " glueht direkt und scheint "
+                     "grosse Schwierigkeiten zu haben.\n" );
+                break;
+                
+            case 11..25 :
+                write( "Du fuehlst Dich beschissen. Alles tut weh, und Du "
+                       "siehst nur noch unscharf.\n" );
+                say( capitalize(name(WER)) + " taumelt und stoehnt und "
+                     "kann gerade noch vermeiden, hinzufallen.\n" );
+                break;
+                
+            case 0..10 :
+                write( break_string( "Du siehst fast nichts mehr und kannst "
+                                     "Dich nur noch unter groessten Schmerzen "
+                                     "bewegen. Aber bald tut nichts mehr weh"
+                                     "...", 78 ) );
+                say( break_string( capitalize(name(WER)) + " glueht wie "
+                                   "im Fieber, kann sich kaum noch ruehren "
+                                   "und hat ein schmerzverzerrtes Gesicht.\n",
+                                   78 ) );
+                break;
+            }
+    }
+
+    SetProp( P_HP, hp );
+    SetProp( P_SP, sp );
+}
+
+public int AddExp( int e )
+{
+  int experience;
+  string fn;
+  mixed last;
+
+  experience = QueryProp(P_XP);
+
+  if ( QueryProp(P_KILLS) > 1 && e > 0 )
+      return experience;
+
+  fn = implode( explode( object_name( environment() || this_object() ),
+                               "/" )[0..<2], "/" );
+
+  if ( pointerp(last = Query(P_LAST_XP)) && sizeof(last) == 2 && last[0] == fn )
+      Set( P_LAST_XP, ({ fn, last[1]+e }) );
+  else
+      Set( P_LAST_XP, ({ fn, e }) );
+
+  if ( (experience += e) < 0 )
+      experience = 0;
+
+  return SetProp( P_XP, experience );
+}
+
+static <string|int>* _set_last_xp( <int|string>* last )
+{
+    if ( !pointerp(last) || sizeof(last) != 2 || !stringp(last[0]) ||
+         !intp(last[1]) )
+        return Query(P_LAST_XP);
+    else
+        return Set( P_LAST_XP, last );
+}
+
+
+static int _set_align(int a)
+{
+  if (a<-1000) a = -1000;
+  if (a>1000) a = 1000;
+  return Set(P_ALIGN, a);
+}
+
+
+static int _set_hp( int hp )
+{
+    if ( QueryProp(P_GHOST) )
+        return QueryProp(P_HP);
+
+    if ( hp < 0 )
+        return Set( P_HP, 0 );
+
+    if ( hp > QueryProp(P_MAX_HP) )
+        return Set( P_HP, QueryProp(P_MAX_HP), F_VALUE );
+
+    return Set( P_HP, hp, F_VALUE );
+}
+
+static int _set_sp( int sp )
+{
+    //einige Leute schreiben floats in die P_HP. :-(
+    if (!intp(sp)) {
+	sp=to_int(sp);
+	//ja, es ist teuer. Aber ich will wissen, wers ist. Kann vor
+	//naechstem Reboot wieder raus.
+	log_file("ILLEGAL_TYPE.log",sprintf(
+	      "Versuch, einen nicht-int in P_SP in %O zu schreiben: \n%O\n",
+	      this_object(),
+	      debug_info(DINFO_TRACE,DIT_STR_CURRENT)));
+    }
+
+    if ( QueryProp(P_GHOST) )
+        QueryProp(P_SP);
+
+    if ( sp < 0 )
+        return Set( P_SP, 0 );
+
+    if ( sp > QueryProp(P_MAX_SP) )
+        return Set( P_SP, QueryProp(P_MAX_SP), F_VALUE );
+
+    return Set( P_SP, sp, F_VALUE );
+}
+
+static int _set_alcohol(int n)
+{
+  if(!intp(n))
+      raise_error(sprintf(
+        "_set_alcohol(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_ALCOHOL, F_VALUE);
+
+  // nur Änderungen und Werte >=0 werden gesetzt...
+  n = n < 0 ? 0 : n;
+  int old = Query(P_ALCOHOL, F_VALUE);
+  if ( old == n)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_ALCOHOL, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_alcohol(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  return Set(P_ALCOHOL, n, F_VALUE);
+}
+
+static int _set_drink(int n)
+{
+  if(!intp(n))
+      raise_error(sprintf(
+        "_set_drink(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_DRINK, F_VALUE);
+
+  // nur Änderungen und Werte >=0 werden gesetzt...
+  n = n < 0 ? 0 : n;
+  int old = Query(P_DRINK, F_VALUE);
+  if ( old == n)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_DRINK, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_drink(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  return Set(P_DRINK, n, F_VALUE);
+}
+
+static int _set_food(int n)
+{
+  if(!intp(n))
+      raise_error(sprintf(
+        "_set_food(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_FOOD, F_VALUE);
+
+  // nur Änderungen und Werte >=0 werden gesetzt...
+  n = n < 0 ? 0 : n;
+  int old = Query(P_FOOD, F_VALUE);
+  if ( old == n)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_FOOD, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_food(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  return Set(P_FOOD, n, F_VALUE);
+}
+
+static int _set_poison(int n)
+{
+    if(!intp(n))
+      raise_error(sprintf(
+        "_set_poison(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_POISON, F_VALUE);
+
+  int mp = QueryProp(P_MAX_POISON);
+  n = (n<0 ? 0 : (n>mp ? mp : n));
+
+  // nur >=0 zulassen.
+  n = n < 0 ? 0 : n;
+
+  int old = Query(P_POISON, F_VALUE);
+  if ( old == 0 && n == 0)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_POISON, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_poison(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  // Fuer die Selbstheilung.
+  switch(n) {
+  case 1:
+    drop_poison = 40+random(16);
+    break;
+  case 2:
+    drop_poison = 25+random(8);
+    break;
+  case 3:
+    drop_poison = 18+random(4);
+    break;
+  default:
+    // nur relevant fuer NPC, da Spieler bei >3 Gift nicht mehr regegenieren.
+    drop_poison = 22 - 2*n + random(43 - 3*n);
+    break;
+  }
+
+  // fuer Setzen der Prop von aussen ein Log schreiben
+  if (previous_object(1) != ME)
+    log_file("POISON", sprintf("%s - %s: %d von %O (%s)\n",
+           dtime(time())[5..],
+           (query_once_interactive(this_object()) ?
+             capitalize(geteuid(this_object())) :
+             capitalize(name(WER))),
+           n,
+           (previous_object(2) ? previous_object(2) : previous_object(1)),
+           (this_player() ? capitalize(geteuid(this_player())) : "???")));
+
+  return Set(P_POISON, n, F_VALUE);
+}
+
+static int _set_xp(int xp) { return Set(P_XP, xp < 0 ? 0 : xp, F_VALUE); }
+
+static mixed _set_die_hook(mixed hook)
+{
+  if(hook && query_once_interactive(this_object()))
+    log_file("DIE_HOOK",
+             sprintf("%s : DIE_HOOK gesetzt von %O in %O (%s)\n",
+                     dtime(time())[5..],
+                     (previous_object(2) ? previous_object(2):previous_object(1)),
+                     this_object(),getuid(this_object())));
+  return Set(P_TMP_DIE_HOOK,hook, F_VALUE);
+}
+
+static mapping _query_enemy_damage()
+{
+  return copy(enemy_damage);
+}
+
+// nur ne Kopie liefern, sonst kann das jeder von aussen aendern.
+static mapping _query_timing_map() {
+  return copy(Query(P_TIMING_MAP));
+}
+
+/****************************************************************************
+ * Consume-Funktion, um zentral durch konsumierbare Dinge ausgeloeste
+ * Aenderungen des Gesundheitszustandes herbeizufuehren.
+ ***************************************************************************/
+
+/* Konsumiert etwas
+ *
+ * Rueckgabewert
+ *      1       erfolgreich konsumiert
+ *      0       fehlende oder falsche Parameter
+ *     <0       Bedingung fuer konsumieren nicht erfuellt, Bitset aus:
+ *      1       Kann nichts mehr essen
+ *      2       Kann nichts mehr trinken
+ *      4       Kann nichts mehr saufen
+ *      8       Abgebrochen durch Hook H_HOOK_CONSUME
+ */
+public varargs int consume(mapping cinfo, int testonly)
+{
+  int retval = 0;
+  // nur was tun, wenn auch Infos reinkommen
+  if (mappingp(cinfo) && sizeof(cinfo)) {
+    // Hooks aufrufen, sie aendern ggf. noch was in cinfo.
+    mixed *hret = HookFlow(H_HOOK_CONSUME, ({cinfo, testonly}) );
+    switch(hret[H_RETCODE])
+    {
+        case H_CANCELLED:
+          return -HC_HOOK_CANCELLATION;
+        case H_ALTERED:
+          // testonly kann nicht geaendert werden.
+          cinfo = hret[H_RETDATA][0];
+    }
+    // Legacy-Mappings (flache) neben strukturierten Mappings zulassen
+    // flache Kopien erzeugen (TODO?: und fuer Teilmappings nicht relevante
+    // Eintraege loeschen)
+    mapping conditions;
+    if (mappingp(cinfo[H_CONDITIONS])) {
+      conditions = copy(cinfo[H_CONDITIONS]);
+    } else {
+      conditions = copy(cinfo);
+    }
+    mapping effects;
+    if (mappingp(cinfo[H_EFFECTS])) {
+      effects = filter(cinfo[H_EFFECTS], (: member(H_ALLOWED_EFFECTS, $1) > -1 :));
+    } else {
+      effects = filter(cinfo, (: member(H_ALLOWED_EFFECTS, $1) > -1 :));
+    }
+
+    // Bedingungen pruefen
+    if (mappingp(conditions) && sizeof(conditions)) {
+      // Bedingungen fuer Konsum auswerten
+      if (conditions[P_FOOD] && !eat_food(conditions[P_FOOD], 1))
+        retval |= HC_MAX_FOOD_REACHED;
+      else if (conditions[P_DRINK] && !drink_soft(conditions[P_DRINK], 1))
+        retval |= HC_MAX_DRINK_REACHED;
+      else if (conditions[P_ALCOHOL] && !drink_alcohol(conditions[P_ALCOHOL], 1))
+        retval |= HC_MAX_ALCOHOL_REACHED;
+      // retval negativ machen, damit Fehler leicht erkennbar ist
+      retval = -retval;
+    }
+    // Bedingungen wurden abgearbeitet, jetzt die Heilung durchfuehren
+    if (!retval) {
+      if (!testonly) {
+        // Bedingungen erfuellen, wenn alles passt und kein Test
+        if (conditions[P_ALCOHOL])
+          drink_alcohol(conditions[P_ALCOHOL]);
+        if (conditions[P_DRINK])
+          drink_soft(conditions[P_DRINK]);
+        if (conditions[P_FOOD])
+          eat_food(conditions[P_FOOD]);
+        // Und jetzt die Wirkungen
+        if (effects[P_POISON])
+          SetProp(P_POISON, QueryProp(P_POISON) + effects[P_POISON]);
+        // Und nun wirklich heilen
+        switch (cinfo[H_DISTRIBUTION]) {
+        case HD_INSTANT:
+          map(effects, (: SetProp($1, QueryProp($1) + $2) :));
+          break;
+        case 1..50:
+          buffer_hp(effects[P_HP], cinfo[H_DISTRIBUTION]);
+          buffer_sp(effects[P_SP], cinfo[H_DISTRIBUTION]);
+          break;
+        default:
+          buffer_hp(effects[P_HP], HD_STANDARD);
+          buffer_sp(effects[P_SP], HD_STANDARD);
+          break;
+        }
+      }
+      retval = 1;
+    }
+  }
+  return retval;
+}
diff --git a/std/living/light.c b/std/living/light.c
new file mode 100644
index 0000000..5b12e1f
--- /dev/null
+++ b/std/living/light.c
@@ -0,0 +1,55 @@
+// MorgenGrauen MUDlib
+//
+// living/description.c -- description for living objects
+//
+// $Id: description.c 7340 2009-11-19 21:44:51Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <living/description.h>
+#include <living/skills.h>
+#undef NEED_PROTOTYPES
+
+#include <player/viewcmd.h>
+#include <new_skills.h>
+#include <container.h>
+#include <player/base.h>
+#include <wizlevels.h>
+
+inherit "/std/container/light";
+
+protected void create() {
+  ::create();
+  SetProp(P_LIGHT_TRANSPARENCY, 0);
+}
+
+static int _query_player_light()
+{
+  if (environment())
+    return environment()->QueryProp(P_INT_LIGHT) + QueryProp(P_LIGHT_MODIFIER);
+}
+
+varargs int CannotSee(int silent)
+{
+   string is_blind;
+   if (is_blind = QueryProp(P_BLIND)) {
+      if (!silent) {
+         if (stringp(is_blind))
+            tell_object(this_object(), is_blind);
+         else tell_object(this_object(), "Du bist blind!\n");
+      }
+      return 2;
+   }
+   if (UseSkill(SK_NIGHTVISION)<=0 &&
+       environment() && QueryProp(P_PLAYER_LIGHT)<=0 &&
+       (!IS_LEARNER(this_object()) || !Query(P_WANTS_TO_LEARN)))
+   {
+       if (!silent) tell_object(this_object(), "Es ist zu dunkel!\n");
+       return 1;
+   }
+   return 0;
+}
diff --git a/std/living/moneyhandler.c b/std/living/moneyhandler.c
new file mode 100644
index 0000000..7a22226
--- /dev/null
+++ b/std/living/moneyhandler.c
@@ -0,0 +1,26 @@
+// MorgenGrauen MUDlib
+//
+// living/moneyhandler.c -- money handler for livings
+//
+// $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
+
+inherit "/std/container/moneyhandler";
+
+// Funktionen sollen nur das Programm ersetzen, natuerlich nur in der
+// Blueprint _dieses_ Objektes, nicht in anderen. ;-) BTW: Normalerweise
+// sollte niemand hierdrin create() rufen, der das Ding hier erbt.
+protected void create_super() {
+  if (object_name(this_object()) == __FILE__[..<3])
+    replace_program();
+}
+
+// wird nicht von erbenden Objekten gerufen. (Wozu auch.)
+protected void create() {
+  create_super();
+}
+
diff --git a/std/living/moving.c b/std/living/moving.c
new file mode 100644
index 0000000..c6149ff
--- /dev/null
+++ b/std/living/moving.c
@@ -0,0 +1,457 @@
+// MorgenGrauen MUDlib
+//
+// living/moving.c -- moving of living objects
+//
+// $Id: moving.c 9448 2016-01-22 17:52:28Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/moving";
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#include <living/moving.h>
+#include <living/skills.h>
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <moving.h>
+#include <new_skills.h>
+#include <living.h>
+
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+
+protected void create()
+{
+    if (object_name(this_object()) == __FILE__[0..<3])
+    {
+      return;
+    }
+    offerHook(H_HOOK_MOVE,1);
+}
+
+public void AddPursuer(object ob)
+{
+  mixed *pur;
+
+  if (!objectp(ob))
+    return;
+
+  if (!pointerp(pur=Query(P_PURSUERS)))
+    pur=({0,({})});
+  else if (member(pur[1],ob)!=-1)
+    return;
+  
+  SetProp(P_PURSUERS,({ pur[0], pur[1]+({ob})-({0}) }));
+  ob->_SetPursued(ME);
+}
+
+public void RemovePursuer(object ob)
+{
+  mixed *pur;
+
+  if (pointerp(pur=Query(P_PURSUERS,F_VALUE)) 
+      && member(pur[1],ob)!=-1)
+  {
+    pur[1]-=({ob,0});
+    if (ob)
+      ob->_RemovePursued(ME);
+    if (!pur[0]&&!sizeof(pur[1]))
+      pur=0;
+    SetProp(P_PURSUERS,pur);
+  }
+}
+
+public void _SetPursued(object ob)
+{
+  mixed *pur;
+
+  if (!pointerp(pur=Query(P_PURSUERS)))
+    pur=({0,({})});
+  else
+    if (objectp(pur[0]))
+      pur[0]->RemovePursuer(ME);
+  pur[0]=ob;
+  pur[1]-=({0});
+  Set(P_PURSUERS,pur);
+}
+
+public void _RemovePursued(object ob)
+{
+  mixed *pur;
+
+  if (!pointerp(pur=Query(P_PURSUERS)) || pur[0]!=ob)
+    return;
+  pur[0]=0;
+  pur[1]-=({0});
+  if (!sizeof(pur[1]))
+    pur=0;
+  Set(P_PURSUERS,pur);
+}
+
+
+private void kampfende( object en ) {
+  if (!objectp(en)) return;
+  tell_object( ME, capitalize(en->name()) +
+      " ist jetzt hinter Dir her.\n" );
+  tell_object( en, "Du verfolgst jetzt " + name(WEN) + ".\n" );      
+  en->InsertSingleEnemy(ME);
+}
+
+private int _is_learner(object pl) {
+  return IS_LEARNER(pl); 
+}
+
+
+// a) Pruefungen, ob das move erlaubt ist.
+// b) zum Ueberschreiben
+protected int PreventMove(object dest, object oldenv, int method) {
+
+  // M_NOCHECK? -> Bewegung eh erlaubt (und Rueckgabewert wuerde ignoriert),
+  // aber PreventInsert/PreventLeave() rufen und ignorieren.
+  if ((method&M_NOCHECK)) {
+      // erst PreventLeaveLiving() rufen...
+      if(environment())        
+          environment()->PreventLeaveLiving(this_object(), dest);
+      // dann PreventInsertLiving() im Ziel-Env.
+      dest->PreventInsertLiving(this_object());
+      // und raus...
+      return(0);
+  }
+
+  // bei Lebewesen muss die Bewegungsmethode M_GO und M_TPORT sein. Dies ist
+  // gleichzeigt die Restriktion gegen das Nehmen von Lebewesen, da dort
+  // M_GET/M_GIVE/M_PUT etc. verwendet wuerde. Bei M_GO und M_TPORT findet
+  // keine Pruefung statt, ob das Objekt ins Ziel 'reinpasst' (Gewicht, Anzahl
+  // Objekte usw.).
+  // Ich finde es etwas merkwuerdig gebaut (Zesstra).
+  if ( !(method & (M_GO | M_TPORT)) )
+      return ME_PLAYER;
+  
+  // alte und neue Umgebung auf NO_TPORT pruefen.
+  if ( (method & M_TPORT) ) {
+    if ( environment() &&
+        (environment()->QueryProp(P_NO_TPORT) & (NO_TPORT_OUT|NO_TPORT)) )
+          return ME_CANT_TPORT_OUT;
+    else if ( dest->QueryProp(P_NO_TPORT) & (NO_TPORT_IN|NO_TPORT) )
+          return ME_CANT_TPORT_IN;
+  }
+
+  // erst PreventLeaveLiving() testen...
+  if( environment() && environment()->PreventLeaveLiving(this_object(), dest))
+      return ME_CANT_LEAVE_ENV;
+  // dann PreventInsertLiving() im Ziel-Env
+  if (dest->PreventInsertLiving(this_object())) 
+      return ME_CANT_BE_INSERTED;
+
+  return 0;
+}
+
+// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
+protected void NotifyMove(object dest, object oldenv, int method) {
+  mixed res;
+  object enem;
+
+  // Begruessungsschlag fuer die Gegener
+  if ( !(method & M_NO_ATTACK) )
+      InitAttack();
+
+  if (!objectp(ME)) return;
+
+  // Verfolger nachholen.
+  if ( pointerp(res = Query(P_PURSUERS)) && sizeof(res[1]) ) {
+      while ( remove_call_out( "TakeFollowers" ) >= 0 );
+
+      call_out( "TakeFollowers", 0 );
+  }
+
+  // und noch das Team nachholen.
+  if ( oldenv != dest
+      && objectp(ME)
+      && QueryProp(P_TEAM_AUTOFOLLOW)
+      && objectp( enem = IsTeamLeader() ) )
+      enem->StartFollow(oldenv); // Teamverfolgung
+
+}
+
+varargs public int move( object|string dest, int method, string direction,
+                         string textout, string textin )
+{
+    int para, nightvis, invis, tmp;
+    object oldenv, *inv;
+    string fn,vc;
+    mixed res;
+    mixed hookData, hookRes;
+
+    if (!objectp(dest) && !stringp(dest))
+      raise_error(sprintf("Wrong argument 1 to move(). 'dest' must be a "
+            "string or object! Argument was: %.100O\n",
+            dest));
+
+    // altes Env erstmal merken.
+    oldenv = environment();
+    
+    //erstmal den richtigen Zielraum suchen, bevor irgendwelche Checks gemacht
+    //werden...
+    // Ist der Spieler in einer Parallelwelt?
+    if ( (para = QueryProp(P_PARA)) && intp(para) ) {
+        fn = objectp(dest) ? object_name(dest) : dest;
+
+        // Falls der Zielraum nicht schon explizit in der Parallelwelt ist,
+        // neuen Zielraum suchen. Aber nur, wenn fn kein # enthaelt (also kein
+        // Clone ist), sonst wuerde eine Bewegung nach raum#42^para versucht,
+        // was dann buggt. ;-) Problem wird offenbar, wenn ein Para-Lebewesen
+        // im create() eines VC-Raums in Para in den Raum bewegt wird, da
+        // dieser dann noch nicht vom Driver umbenannt wurde und raum#42
+        // heisst.
+        if ( !sizeof(regexp( ({ fn }), "\\^[1-9][0-9]*$" )) &&
+            strrstr(fn,"#")==-1 )
+        {
+            fn += "^" + para;
+
+          // Der Parallelwelt-Raum muss existieren und fuer Spieler
+          // freigegeben sein, damit er zum neuen Ziel wird. Ansonsten
+          // duerfen nur NPCs, Testspieler und Magier herein.
+          if ( (find_object(fn) 
+                || ((file_size(fn+".c")>0 ||
+                    (file_size(vc=implode(explode(fn,"/")[0..<2],"/")+
+                          "/virtual_compiler.c")>0 &&
+                    !catch(tmp=(int)call_other(vc,"QueryValidObject",fn);
+                           publish) && tmp>0)) &&
+                    !catch(load_object(fn);publish) )) &&
+                  (!interactive(ME) || !fn->QueryProp(P_NO_PLAYERS) || 
+                  (method & M_NOCHECK) || IS_LEARNER(ME) ||
+                  (stringp(res = QueryProp(P_TESTPLAYER)) &&
+                   IS_LEARNER( lower_case(res) ))) )
+          {
+              dest = fn;
+          }
+          else
+          {
+              // Wir bleiben in der Normalwelt.
+              para = 0;
+          }
+        }
+    }
+
+    // jetzt erstmal Hooks abpruefen, da sie ggf. die Daten aendern.
+    // alten P_TMP_MOVE_HOOK pruefen.
+    if ( res = QueryProp(P_TMP_MOVE_HOOK) ){
+        if ( pointerp(res) && sizeof(res) >= 3
+             && intp(res[0]) && time()<res[0]
+             && objectp(res[1]) && stringp(res[2]) ){
+            if ( res = call_other( res[1], res[2], dest, method, direction,
+                                   textout, textin ) ){
+                if ( pointerp(res) && sizeof(res) == 5 ){
+                    dest = res[0];
+                    method = res[1];
+                    direction = res[2];
+                    textout = res[3];
+                    textin = res[4];
+                }
+                else if ( intp(res) && res == -1 )
+                    return ME_CANT_LEAVE_ENV;
+            }
+        } else
+            SetProp( P_TMP_MOVE_HOOK, 0 );
+    }
+    // move hook nach neuem Hooksystem triggern.
+    hookData=({dest,method,direction,textout,textin});
+    hookRes=HookFlow(H_HOOK_MOVE,hookData);
+    if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA) {
+      if(hookRes[H_RETCODE]==H_CANCELLED) {
+        return ME_CANT_LEAVE_ENV;
+      }
+    else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] &&
+          pointerp(hookRes[H_RETDATA]) && sizeof(hookRes[H_RETDATA])>=5 ){
+      dest = hookRes[H_RETDATA][0];
+      method = hookRes[H_RETDATA][1];
+      direction = hookRes[H_RETDATA][2];
+      textout = hookRes[H_RETDATA][3];
+      textin = hookRes[H_RETDATA][4];
+      }
+    }
+
+    // dest auf Object normieren
+    if (stringp(dest)) dest=load_object(dest);
+
+    // jetzt Checks durchfuehren, ob das Move durchgefuehrt werden darf.
+    if (tmp=PreventMove(dest, oldenv, method)) {
+      // auf gueltigen Fehler pruefen, wer weiss, was Magier da evtl.
+      // versehentlich zurueckgeben.
+      if (VALID_MOVE_ERROR(tmp))
+        return(tmp);
+      else
+        return(ME_DONT_WANT_TO_BE_MOVED);
+    }
+  
+    if ( invis = QueryProp(P_INVIS) )
+        method |= M_SILENT;
+
+    if ( objectp(oldenv) ) {
+        if ( !(method & M_SILENT) ) {
+            string *mout;
+            if ( !textout ){
+                if ( method & M_TPORT )
+                    textout = (string) QueryProp(P_MMSGOUT) ||
+                        (string) QueryProp(P_MSGOUT);
+                else 
+                    textout = (mout = explode( (string)
+                                                QueryProp(P_MSGOUT) || "",
+                                                      "#" ))[0]
+                         || (string)QueryProp(P_MMSGOUT);
+            }
+
+            if ( !sizeof(direction) )
+                direction = 0;
+
+            inv = all_inventory(environment()) - ({ this_object() });
+            inv = filter( inv, #'living/*'*/ );
+            inv -= filter_objects( inv, "CannotSee", 1 );
+
+            filter( inv, #'tell_object/*'*/,
+                          Name( WER, 2 ) + " " + textout +
+                          (direction ? " " + direction : "") +
+                          (sizeof(mout) > 1 ? mout[1] : "") + ".\n" );
+        }
+        // Magier sehen auch Bewegungen, die M_SILENT sind
+        else if ( interactive(ME) ){
+            inv = (all_inventory(environment()) & users())
+                - ({ this_object() });
+            inv = filter( inv, #'_is_learner/*'*/ );
+
+            if ( invis )
+                fn = "(" + capitalize(getuid(ME)) + ") verschwindet "
+                    "unsichtbar.\n";
+            else
+                fn = capitalize(getuid(ME)) + " verschwindet ganz leise.\n";
+            
+            filter( inv, #'tell_object/*'*/, fn );
+        }
+        
+        // Nackenschlag beim Fluechten:
+        if ( !(method & M_NO_ATTACK) && objectp(ME) )
+            ExitAttack();
+        //falls nach ExitAttack() das Living nicht mehr existiert, muss das
+        //move() auch nicht mehr fortgesetzt werden. Weiter unten gibt es auch
+        //min. eine Stelle, die nicht prueft und ggf. buggt. Daher erfolgt
+        //hier ggf. ein Abbruch. 15.11.06 Zesstra
+        if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
+
+        // Nackenschlag kann ME in den Todesraum bewegt haben...
+        if ( oldenv == environment() ) {
+            // Fuer alle anwesenden gegner kampfende() aufrufen
+            filter((QueryEnemies()[0] & all_inventory(oldenv))-({0}),
+                #'kampfende);
+            // Bugs im exit() sind ohne catch() einfach mist.
+            catch(environment()->exit(ME, dest);publish);
+        }
+    }
+
+    // irgendwas kann das Objekt zerstoert haben, z.B. env->exit().
+    if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
+
+    if ( oldenv != environment() )
+        // Der Nackenschlag oder exit() koennen einen schon bewegt haben.
+        // Und wenn es in den Todesraum ist. ;^)
+        return MOVE_OK;
+    
+    SetProp( P_PREPARED_SPELL, 0 ); // Spruchvorbereitung abgebrochen
+    SetProp( P_LAST_MOVE, time() ); // Zeitpunkt der letzten Bewegung
+    
+    move_object(ME, dest);
+    if (!objectp(ME))
+      return(ME_WAS_DESTRUCTED);
+
+    dest = environment();
+    
+    nightvis = UseSkill(SK_NIGHTVISION);
+    // Meldungen an nicht-Blinde ausgeben, falls keine 'stille' Bewegung
+    if ( !(method & M_SILENT) ) {
+      if ( !textin ) {        
+        if ( method & M_TPORT )
+              textin = (string) QueryProp(P_MMSGIN);
+        else
+              textin = (string) QueryProp(P_MSGIN);
+      }
+            
+      inv = all_inventory(environment()) - ({ this_object() });
+      inv = filter( inv, #'living/*'*/ );
+            inv -= filter_objects( inv, "CannotSee", 1 );
+            filter( inv, #'tell_object/*'*/,
+                          capitalize(name( WER, 0 )) + " " + textin + ".\n" );
+    }
+    // sonst: Magier sehen auch M_SILENT-Bewegungen, hier also nur an Magier
+    // ausgeben, alle anderen sehen eh nix.
+    else if ( interactive(ME) ) {  
+      inv = (all_inventory(environment()) & users()) - ({this_object()});        
+      inv = filter( inv, #'_is_learner/*'*/ );        
+      if ( invis )
+        fn = "(" + capitalize(getuid(ME)) + ") taucht unsichtbar auf.\n";
+      else
+        fn = capitalize(getuid(ME)) + " schleicht leise herein.\n";        
+      filter( inv, #'tell_object, fn );    
+    }
+
+    // "Objekt" ueber das Move informieren.
+    NotifyMove(dest, oldenv, method);
+
+    // InitAttack() in NotifyMove() kann das Objekt zerstoert haben.
+    if (!objectp(ME))
+      return(ME_WAS_DESTRUCTED);
+
+    //scheint wohl geklappt zu haben.
+    return MOVE_OK;
+}
+
+public void TakeFollowers()
+{
+  mixed *f,env;
+  int meth,i,r;
+
+  f=Query(P_PURSUERS);
+  if (!pointerp(f))
+    return;
+  env=environment();
+  if(object_name(env) == "/room/netztot") return;
+  foreach(object follower: f[1]-({0}) ) {
+    // die pruefung auf objectp ist nicht verrueckt, es kann theo. sein, dass
+    // Verfolger im PreventFollow() oder in ihrem move/init andere Verfolger
+    // zerstoeren.
+    if (objectp(follower) && environment(follower)!=env) {
+      //meth=M_NOCHECK;
+      meth=M_GO;
+      if (follower->Query(P_FOLLOW_SILENT))
+          meth|=M_SILENT|M_NO_SHOW;
+      catch(r=follower->PreventFollow(env);publish);
+      if (!r)
+          follower->move(env,meth);
+      else if (r==2)
+          RemovePursuer(follower);
+    }
+  }
+}
+
+varargs public int remove()
+{ object team;
+
+  if (environment())
+  {
+    if ( objectp(team=Query(P_TEAM)) )
+      catch(team->RemoveMember(ME);publish);
+
+    environment()->NotifyRemove(ME);
+  }
+  destruct(ME);
+  return 1;
+}
+
diff --git a/std/living/put_and_get.c b/std/living/put_and_get.c
new file mode 100644
index 0000000..4e1a97f
--- /dev/null
+++ b/std/living/put_and_get.c
@@ -0,0 +1,1243 @@
+// MorgenGrauen MUDlib
+//
+// living/put_and_get.c -- taking and putting things
+//
+// $Id: put_and_get.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+/*
+  Grundlegend neu strukturiert von Amynthor im April-Juni 2007
+
+Die eigentlichen Funktionen:
+
+  private string put_or_get(object o, object dest)
+    Bewegt ein einzelnes Objekt mit automatisch bestimmter Method. Gibt im
+    Erfolgsfall 0 zurueck, sonst die auszugebende Fehlermeldung.
+
+  varargs int drop(object o, mixed msg)
+  varargs int put(object o, object dest, mixed msg)
+  varargs int pick(object o, mixed msg)
+  varargs int give(object o, object dest, mixed msg)
+  varargs int show(object o, object dest, mixed msg)
+    Der Spieler nimmt/legt/gibt/zeigt/laesst ein Objekt fallen, wobei die
+    entsprechenden oder optional abweichende (im Format von P_XXX_MSG) oder
+    gar keine (msg == 1) Meldungen ausgegeben werden. Es liegt in der
+    Verantwortung des Rufenden, sinnvolle Werte zu uebergeben; insbesondere
+    wird nicht geprueft, ob sich die Objekte in der Reichweite des Spielers
+    befinden. Gibt 1 zurueck, wenn das Objekt bewegt wurde, sonst 0.
+
+Hilfsfunktionen:
+
+  private object *__find_objects(string *tokens, object env, int is_source)
+  object *find_objects(string what, object env, int is_source)
+    Sucht im Raum und im Spieler (oder alternativ in der angegebenen Umgebung)
+    nach den in tokens/what bezeichneten Objekten. is_source bestimmt die
+    erwartete grammatische Form (0 fuer "topf auf herd" und 1 fuer "topf von
+    herd", siehe Manpage).
+
+  varargs int drop_objects(string str, mixed msg)
+  varargs int put_objects(string str, int casus, string verb, mixed msg)
+  varargs int pick_objects(string str, mixed msg, int flag)
+  varargs int give_objects(string str, mixed msg)
+  varargs int show_objects(string str, mixed msg)
+    Ein Befehl wie "wirf waffen weg" resultiert in einem Aufruf von
+    drop_objects("waffen"). Diese Funktionen sind hauptsaechlich fuer die
+    Behandlung der jeweiligen Kommandos vorgesehen, koennen jedoch auch fuer
+    eigene Befehle verwendet werden. put_objects() erwartet ausserdem den
+    Kasus ("Du kannst nichts an DER Pinwand befestigen.") und das verwendete
+    Verb in der Gundform ("befestigen"). Das Flag fuer pick_objects() gibt
+    an, ob das Objekt auch einfach herumliegen darf ("nimm ...") oder nicht
+    ("hole ..."). Gibt bei Erfolg 1, sonst 0 zurueck.
+
+  object *moved_objects()
+  object moved_where()
+    Gibt die eben fallengelassenen/gesteckten/... Objekte zurueck und wohin
+    sie gesteckt/wem sie gegeben/gezeigt wurden. Fuer den Fall, dass man
+    anschliessend noch etwas mit ihnen machen moechte.
+
+Die einzelnen Kommandos:
+  static int fallenlassen(string str)
+  static int werfen(string str)
+  static int legen(string str)
+  static int stecken(string str)
+  static int holen(string str)
+  static int nehmen(string str)
+  static int geben(string str)
+    Minimale Wrapper fuer XXX_objects(), entfernen "fallen", "weg" bzw. "ab"
+    aus den Argumenten und setzen entsprechende Standard-Fehlermeldungen.
+    
+  protected void add_put_and_get_commands()
+    Registriert obige Funktionen per add_action().
+
+Aus reinen Kompatibilitaetsgruenden weiterhin enthalten:
+
+  object* find_obs(string str, int meth)
+  int pick_obj(object ob)
+  int drop_obj(object ob)
+  int put_obj(object ob, object where)
+  int give_obj(object ob, object where)
+    siehe Manpages
+
+*/
+
+/*
+  21. Okt 1998 komplette neu programmierung von put_and_get.c (Padreic)
+- die Gruppenauswahlen alles, waffen und ruestungen sind jetzt immer moeglich
+  die Gruppen sind sehr leicht erweiterbar und man sollte sich nicht scheuen
+  davon gebrauch zu machen...
+- mit "in mir" und "im raum" kann man den abzusuchenden Raum selbst eingrenzen
+- mit "alle|jede|jeden|jedes <id>" kann man auch ganze objektgruppen auswaehlen
+*/
+
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <language.h>
+#include <thing/description.h>
+#include <thing/properties.h>
+#include <moving.h>
+#include <container.h>
+#undef NEED_PROTOTYPES
+
+#include <defines.h>
+#include <properties.h>
+#include <wizlevels.h>
+
+#define TME(str) tell_object(this_object(), \
+    break_string(str, 78, 0, BS_LEAVE_MY_LFS))
+#define TOB(ob,str) tell_object(ob, break_string(str, 78, 0, BS_LEAVE_MY_LFS))
+#define SAY(str) tell_room(environment(), \
+    break_string(str, 78, 0, BS_LEAVE_MY_LFS), ({this_object()}))
+#define SAY2(obs, str) tell_room(environment(), \
+    break_string(str, 78, 0, BS_LEAVE_MY_LFS), ({this_object()}) + obs)
+#define NF(str) _notify_fail(break_string(str, 78, 0, BS_LEAVE_MY_LFS))
+
+private nosave closure cl;
+private nosave string wen0, wen1, wer0;
+private nosave object *last_moved_objects;
+private nosave object last_moved_where;
+
+
+/*********************** Die eigentlichen Funktionen ************************/
+
+private string put_or_get(object o, object dest)
+
+/* Bewegt ein einzelnes Objekt <o> in das Zielobjekt <dest>. Verwendet dazu
+ * je nach Umstaenden (Ziel ist der Spieler, die Umgebung, ein Lebewesen) den
+ * entsprechenden Wert fuer <method>. Gibt im Erfolgsfall 0 zurueck, erstellt
+ * sonst die auszugebende Fehlermeldung und gibt diese zurueck.
+ */
+
+{
+    int method, ret;
+    string str;
+
+    //if (living(o))
+    //    raise_error(sprintf("Lebendes Argument fuer put_or_get: %O\n", o));
+
+    if (dest == this_object())          /* pick */
+        method = M_GET;
+    else if (dest == environment())     /* drop */
+        method = M_PUT;
+    else if (living(dest))              /* give */
+        method = M_GIVE;
+    else {                              /* put */
+        method = M_PUT | M_GET;
+        if (first_inventory(o))
+            return o->Name(WER, 1) + " ist nicht leer!";
+    }
+
+    if ((ret = o->move(dest, method)) > 0)
+        return 0;
+
+    switch (ret) {
+        case ME_TOO_HEAVY:
+            if (dest == this_object())
+                if (QueryProp(P_GHOST))
+                    return "Als Geist kannst Du nichts mitnehmen.";
+                else
+                    return "Du kannst " + wen1 + " nicht mehr tragen.";
+
+            if (stringp(str = dest->QueryProp(P_TOO_HEAVY_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (living(dest)) {
+                if (dest->QueryProp(P_GHOST))
+                    return "Als Geist kann " + dest->name(WER, 1) +
+                           " nichts mitnehmen.";
+                else
+                    return dest->Name(WER, 1) + " kann " +
+                           wen0 + " nicht mehr tragen.";
+            }
+
+            if (dest == environment())
+                return (stringp(str = dest->Name(WER, 1)) && sizeof(str) ?
+                        str : "Der Raum") + " wuerde dann zu schwer werden.";
+
+            return capitalize(wer0 + " passt in " + dest->name(WEN, 1) +
+                              " nicht mehr rein.");
+
+        case ME_CANT_BE_DROPPED:
+            if (o && stringp(str = o->QueryProp(P_NODROP)))
+                return str;
+
+            if (dest == environment())
+                return "Du kannst " + wen1 + " nicht wegwerfen!";
+
+            if (living(dest))
+                return "Du kannst " + wen1 + " nicht weggeben.";
+
+            return "So wirst Du " + wen1 + " nicht los...";
+
+        case ME_CANT_BE_TAKEN:
+            if (o && stringp(str = o->QueryProp(P_NOGET)))
+                return str;
+
+            if (stringp(str = environment(o)->QueryProp(P_NOLEAVE_MSG)))
+                return capitalize(
+                    replace_personal(str, ({o, environment(o)}), 1));
+
+            //if (dest != environment())
+            //    return "Du kannst " + wen1 + " nicht einmal nehmen.";
+
+            return "Du kannst " + wen1 + " nicht nehmen.";
+
+        case ME_CANT_BE_INSERTED:
+            if (stringp(str = dest->QueryProp(P_NOINSERT_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (dest == environment())
+                return "Das darfst Du hier nicht ablegen.";
+
+            if (dest == this_object())
+                return "Du kannst " + wen1 + " nicht nehmen.";
+
+            if (living(dest))
+                return "Das kannst Du " + dest->name(WEM, 1) + " nicht geben.";
+
+            return capitalize(wen0 + " kannst Du dort nicht hineinstecken.");
+
+        case ME_CANT_LEAVE_ENV:
+            // ME_CANT_LEAVE_ENV kann nur auftreten, wenn o ein Environment
+            // hat, deshalb kein Check dadrauf
+            if (stringp(str = environment(o)->QueryProp(P_NOLEAVE_MSG)))
+                return capitalize(
+                    replace_personal(str, ({o, environment(o)}), 1));
+
+            if (environment(o) != this_object())
+                return "Du kannst " + wen1 + " nicht nehmen.";
+
+            if (dest == environment())
+                return "Du kannst " + wen1 + " nicht wegwerfen!";
+
+            if (living(dest))
+                return "Du kannst " + wen1 + " nicht weggeben.";
+
+            return "So wirst Du " + wen1 + " nicht los...";
+
+        case ME_TOO_HEAVY_FOR_ENV:
+            if (stringp(str = dest->QueryProp(P_ENV_TOO_HEAVY_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (environment(dest) == this_object())
+                return dest->Name(WER, 1) +
+                       " wuerde Dir dann zu schwer werden.";
+
+            return (stringp(str = environment(dest)->Name(WER, 1))
+                        && sizeof(str) ? str : "Der Raum") +
+                    " wuerde dann zu schwer werden.";
+
+        case TOO_MANY_OBJECTS:
+            if (stringp(str = dest->QueryProp(P_TOO_MANY_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (dest == this_object())
+                return "Soviele Gegenstaende kannst Du unmoeglich tragen!";
+
+            if (dest == environment())
+                return "Dafuer ist hier nicht mehr genug Platz.";
+
+            if (living(dest))
+                return dest->Name(WER, 1) + " kann "  + wen0 +
+                       " nicht mehr tragen.";
+
+            return "Dafuer ist nicht mehr genug Platz in " +
+                   dest->name(WEM, 1) + ".";
+
+        default:
+            if (dest == this_object())
+                return "Du kannst " + wen1 + " nicht nehmen.";
+
+            if (dest == environment())
+                return "Du kannst " + wen1 + " nicht wegwerfen!";
+
+            if (living(dest))
+                return "Du kannst " + wen1 + " nicht weggeben.";
+
+            return capitalize(wen0 + " kannst Du dort nicht hineinstecken.");
+    }
+    return 0; // NOT REACHED
+}
+
+
+/* varargs int drop(object o, mixed msg)
+ * varargs int put(object o, object dest, mixed msg)
+ * varargs int pick(object o, mixed msg)
+ * varargs int give(object o, object dest, mixed msg)
+ * varargs int show(object o, object dest, mixed msg)
+ *
+ * Der Spieler nimmt/legt/gibt/zeigt/laesst ein Objekt fallen, wobei die
+ * entsprechenden oder optional abweichende (im Format von P_XXX_MSG) oder
+ * gar keine (msg == 1) Meldungen ausgegeben werden. Es liegt in der
+ * Verantwortung des Rufenden, sinnvolle Werte zu uebergeben; insbesondere
+ * wird nicht geprueft, ob sich die Objekte in der Reichweite des Spielers
+ * befinden. Gibt 1 zurueck, wenn das Objekt bewegt wurde, sonst 0.
+ */
+
+varargs int drop(object o, mixed msg)
+{
+    string str;
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = funcall(cl, WEN, 0);
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = 0;
+
+    if (!msg)
+        msg = o->QueryProp(P_DROP_MSG);
+
+    if (str = put_or_get(o, environment())) {
+        TME(str);
+        return 0;
+    }
+
+    if (!msg) {
+        TME("Du laesst " + wen1 + " fallen.");
+        SAY(Name(WER,1) + " laesst " + wen0 + " fallen.");
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          // Wenn es zwei Strings gibt, geht die 2. ans Environment
+          case 2:
+            SAY(replace_personal(msg[1], ({this_object(), o||wen0}), 1));
+          case 1:
+            TME(replace_personal(msg[0], ({this_object(), o||wen1}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_DROP_MSG: %O\n", o||wen1));
+        }
+
+    return 1;
+}
+
+varargs int put(object o, object dest, mixed msg)
+{
+    string str;
+
+    // Falls das jemand von aussen ruft und Schrott uebergibt...
+    //if (living(dest))
+    //    raise_error(sprintf("Lebendes Ziel fuer put(): %O\n", dest));
+    if (dest == environment())
+        raise_error("Ziel fuer put() ist Umgebung des Spielers\n");
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = funcall(cl, WEN, 0);
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = funcall(cl, WER, 0);
+
+    if (!msg)
+        msg = o->QueryProp(P_PUT_MSG);
+
+    if (str = put_or_get(o, dest)) {
+        TME(str);
+        return 0;
+    }
+
+  
+    if (!msg) {
+        TME("Du steckst " + wen1 + " in " + dest->name(WEN, 1) + ".");
+        if (environment())
+	  SAY(Name(WER, 1) + " steckt " + wen0 +
+	      " in " + dest->name(WEN, 0) + ".");
+    }
+    else if (pointerp(msg)) {
+        switch (sizeof(msg)) {
+          case 2:
+            if (environment())
+	      SAY(replace_personal(msg[1], ({this_object(), o||wen0, dest}), 1));
+          case 1:
+            TME(replace_personal(msg[0], ({this_object(), o||wen1, dest}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_PUT_MSG: %O\n",o||wen1));
+        }
+    }
+
+    return 1;
+}
+
+varargs int pick(object o, mixed msg)
+{
+    string str;
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = 0;
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = 0;
+
+    if (!msg)
+        msg = o->QueryProp(P_PICK_MSG);
+
+    if (str = put_or_get(o, this_object())) {
+        TME(str);
+        return 0;
+    }
+
+    if (!msg) {
+        TME("Du nimmst " + wen1 + ".");
+        SAY(Name(WER, 1) + " nimmt " + wen1 + ".");
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          case 2:
+            SAY(replace_personal(msg[1], ({this_object(), o||wen1}), 1));
+          case 1:
+            TME(replace_personal(msg[0], ({this_object(), o||wen1}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_PICK_MSG: %O\n", o||wen1));
+        }
+
+    return 1;
+}
+
+varargs int give(object o, object dest, mixed msg)
+{
+    string zname, gname;
+    string str;
+
+    // Falls das jemand von aussen ruft und Schrott uebergibt...
+    if (!living(dest))
+        raise_error(sprintf("Totes Ziel fuer give(): %O\n", dest));
+
+    zname = dest->name(WEM, 1);
+    gname = Name(WER, 1);
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = funcall(cl, WEN, 0);
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = 0;
+
+    if (!msg)
+        msg = o->QueryProp(P_GIVE_MSG);
+
+    if (str = put_or_get(o, dest)) {
+        TME(str);
+        return 0;
+    }
+
+    if (!msg) {
+        TME("Du gibst " + zname + " " + wen1 + ".");
+        TOB(dest, gname + " gibt Dir " + wen0 + ".");
+        SAY2(({dest}), gname + " gibt " + zname + " " + wen0 + ".");
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          case 3:
+            TOB(dest, replace_personal(
+                msg[2], ({this_object(), o||wen0, dest||zname}), 1));
+          case 2:
+            SAY2(({dest, this_object()}), replace_personal(
+                 msg[1], ({this_object(), o||wen0, dest||zname}), 1));
+          case 1:
+            TME(replace_personal(
+                msg[0], ({this_object(), o||wen1, dest||zname}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_GIVE_MSG: %O\n", o||wen1));
+        }
+
+    if (!query_once_interactive(dest))
+        dest->give_notify(o);
+
+    return 1;
+}
+
+varargs int show(object o, object whom, mixed msg)
+{
+    string zname, gname;
+    string wen0, wen2, long;
+
+    zname = whom ? whom->name(WEM, 1) : "allen";
+    gname = Name(WER, 1);
+
+    if (!msg)
+        msg = o->QueryProp(P_SHOW_MSG);
+
+    if (environment(o) == this_object() ||
+        environment(environment(o)) == this_object()) {
+        wen0 = o->name(WEN, 0);
+
+        /* Der Akkusativ muss mit dem unbestimmten Artikel gebildet werden,
+         * damit eventuelle Adjektive die richtige Endung besitzen.
+         * (ein kleines Schwert -> kleines Schwert -> Dein kleines Schwert)
+         */
+
+        if (o->QueryProp(P_ARTICLE) && member(wen0, ' ') >= 0) {
+            int obgender = o->QueryProp(P_GENDER);
+            int obnum = o->QueryProp(P_AMOUNT) > 1 ? PLURAL : SINGULAR;
+
+            // Wichtig: P_AMOUNT ist 0 fuer Objekte, die nicht von unit erben.
+            // Da unit.c kein P_AMOUNT==0 zulaesst, nehmen wir diesen Fall als
+            // singular an. *Rumata
+
+            wen2 = wen0[member(wen0, ' ')..];
+            wen0 = QueryPossPronoun(o, WEN, obnum) + wen2;
+
+            if (obnum == PLURAL || obgender == FEMALE)
+                wen2 = "Deine" + wen2;
+            else if (obgender == MALE)
+                wen2 = "Deinen" + wen2;
+            else
+                wen2 = "Dein" + wen2;
+        } else
+            wen2 = wen0;
+    } else
+        wen2 = wen0 = o->name(WEN, 1);
+
+    // vorher speichern, falls das Objekt im catch_tell() zerstoert wird
+    long = o->long(4);
+
+    if (!msg) {
+        TME("Du zeigst " + zname + " " + wen2 + ".");
+        if (!whom)
+            SAY(gname + " zeigt Dir " + wen0 + ".");
+        else {
+            TOB(whom, gname + " zeigt Dir " + wen0 + ".");
+            SAY2(({whom}), gname + " zeigt " + zname + " " + wen0 + ".");
+        }
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          case 3:
+            if (whom)
+                TOB(whom, replace_personal(
+                    msg[2], ({this_object(), o||wen0, whom||zname}), 1));
+            else
+                SAY(replace_personal(
+                    msg[2], ({this_object(), o||wen0, whom||zname}), 1));
+          case 2:
+            if (whom)
+                SAY2(({whom, this_object()}), replace_personal(
+                     msg[1], ({this_object(), o||wen0, whom||zname}), 1));
+          case 1:
+            TME(replace_personal(
+                msg[0], ({this_object(), o||wen2, whom||zname}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_SHOW_MSG: %O\n", o||wen0));
+        }
+
+    if (!whom)
+        SAY(long);
+    else {
+        TOB(whom, long);
+        if (!query_once_interactive(whom))
+            whom->show_notify(o);
+    }
+
+    return 1;
+}
+
+
+/***************************** Hilfsfunktionen *****************************/
+
+/* private object *__find_objects(string *tokens, object env, int is_source);
+ * object *find_objects(string what, object env, int is_source);
+ *
+ * Sucht im Raum und im Spieler (oder alternativ in der angegebenen Umgebung)
+ * nach den in tokens/what bezeichneten Objekten. is_source bestimmt die
+ * erwartete grammatische Form (0 fuer "topf auf herd" und 1 fuer "topf von
+ * herd", siehe Manpage).
+ */
+ 
+private object *__find_objects(string *tokens, object env, int is_source)
+{
+    object ob, *obs;
+
+    // is_source == 0: Objekt soll nicht bewegt werden ("topf auf herd")
+    //              1: Objekt soll bewegt werden ("topf von herd")
+    //              2: intern
+
+    if (!env && sizeof(tokens) > 1 && tokens[<1] == "hier") {
+        tokens = tokens[..<2];
+        env = environment();
+    }
+    else if (!env && sizeof(tokens) > 2 && tokens[<2] == "in")
+    {
+        if (tokens[<1] == "mir" ||
+            tokens[<1] == "dir") {
+            tokens = tokens[..<3];
+            env = this_object();
+        }
+        else if (tokens[<1] == "raum") {
+            tokens = tokens[..<3];
+            env = environment();
+        }
+    }
+
+    for (int i = sizeof(tokens)-1; i > 1; i--) {
+        if (env)
+            ob = present(implode(tokens[i..], " "), env);
+        else
+            ob = present(implode(tokens[i..], " "), environment()) ||
+                 present(implode(tokens[i..], " "), this_object());
+
+        if (!ob)
+            continue;
+
+        if (living(ob)) {
+            NF("Aber " + ob->name(WER, 1) + " lebt doch!");
+            continue;
+        }
+
+        if (ob->QueryProp(P_CNT_STATUS)) {
+            NF("Aber " + ob->name(WER, 1) + " ist doch geschlossen!");
+            continue;
+        }
+
+        if (is_source != 0 &&
+            tokens[i-1] == ob->QueryProp(P_SOURCE_PREPOSITION))
+            return ob->present_objects(implode(tokens[..i-2], " "));
+
+        if (tokens[i-1] == ob->QueryProp(P_PREPOSITION))
+            return __find_objects(tokens[..i-2], ob, is_source ? 2 : 0);
+
+        NF("Du kannst nichts " + tokens[i-1] + " " +
+           ob->name(WEM, 1) + " nehmen.");
+    }
+
+    if (is_source == 2)
+        return ({});
+
+    if (env)
+        return env->present_objects(implode(tokens, " "));
+
+    if (environment() &&
+        sizeof(obs = environment()->present_objects(implode(tokens, " "))))
+        return obs;
+
+    return present_objects(implode(tokens, " "));
+}
+
+object *find_objects(string what, object env, int is_source)
+{
+  if (!stringp(what) || !sizeof(what))
+    return ({});
+  return __find_objects(explode(what, " "), env, is_source);
+}
+
+
+/* varargs int drop_objects(string str, mixed msg);
+ * varargs int put_objects(string str, int casus, string verb, mixed msg);
+ * varargs int pick_objects(string str, int flag, mixed msg);
+ * varargs int give_objects(string str, mixed msg);
+ * varargs int show_objects(string str, mixed msg);
+ *
+ * Ein Befehl wie "wirf waffen weg" resultiert in einem Aufruf von
+ * drop_objects("waffen"). Diese Funktionen sind hauptsaechlich fuer die
+ * Behandlung der jeweiligen Kommandos vorgesehen, koennen jedoch auch fuer
+ * eigene Befehle verwendet werden. put_objects() erwartet ausserdem den
+ * Kasus ("Du kannst nichts an DER Pinwand befestigen.") und das verwendete
+ * Verb in der Gundform ("befestigen"). Das Flag fuer pick_objects() gibt
+ * an, ob das Objekt auch einfach herumliegen darf ("nimm ...") oder nicht
+ * ("hole ..."). Gibt bei Erfolg 1, sonst 0 zurueck.
+ */
+
+varargs int drop_objects(string str, mixed msg)
+{
+    object *obs;
+
+    if (!sizeof(obs = find_objects(str, this_object(), 1)))
+        return 0;
+
+    foreach (object o: obs) {
+        if (objectp(o))
+            drop(o, msg);
+
+        if (get_eval_cost() < 100000) {
+            TME("Den Rest behaeltst Du erst mal.");
+            last_moved_objects = obs[..member(obs, o)];
+            last_moved_where = 0;
+            return 1;
+        }
+    }
+
+    last_moved_objects = obs;
+    last_moved_where = 0;
+    return 1;
+}
+
+varargs int put_objects(string str, int casus, string verb, mixed msg)
+{
+    object *obs, dest, *no_move;
+    
+    if (!stringp(str) || !sizeof(str)) return 0;
+
+    string *tokens = explode(str, " ");
+    int allow_room = 1;
+    int allow_me = 1;
+
+    if (sizeof(tokens) > 1 && tokens[<1] == "hier") {
+        tokens = tokens[..<2];
+        allow_me = 0;
+    } else if (sizeof(tokens) > 2 && tokens[<2] == "in")
+        if (tokens[<1] == "mir" ||
+            tokens[<1] == "dir") {
+            tokens = tokens[..<3];
+            allow_room = 0;
+        } else if (tokens[<1] == "raum") {
+            tokens = tokens[..<3];
+            allow_me = 0;
+        }
+
+    for (int i = sizeof(tokens)-1; i > 1; i--) {
+        if (!(dest = allow_room && present(implode(tokens[i..], " "),
+                                           environment())) &&
+            !(dest = allow_me && present(implode(tokens[i..], " "),
+                                         this_object())))
+            continue;
+
+        if (living(dest)) {
+            NF("Aber " + dest->name(WER, 1) + " lebt doch!");
+            continue;
+        }
+/*
+        if (verb == "legen" && !dest->QueryProp(P_TRAY)) {
+            NF("Du kannst nichts auf " + dest->name(WEN, 1) + " legen.");
+            continue;
+        }
+*/
+        if (verb == "stecken" && !dest->QueryProp(P_CONTAINER)) {
+            NF("Du kannst in " + dest->name(WEN, 1) + " nichts reinstecken.");
+            continue;
+        }
+
+        if (dest->QueryProp(P_CNT_STATUS)) {
+            NF("Aber " + dest->name(WER, 1) + " ist doch geschlossen!");
+            continue;
+        }
+
+        if (tokens[i-1] != dest->QueryProp(P_DEST_PREPOSITION)) {
+            NF("Du kannst nichts " + tokens[i-1] + " " +
+               dest->name(casus, 1) + " " + verb + ".");
+            continue;
+        }
+
+        if (!sizeof(obs = __find_objects(tokens[..i-2], 0, 1) - ({ dest }))) {
+            NF("WAS moechtest Du " + tokens[i-1] + " " +
+               dest->name(casus, 1) + " " + verb + "?");
+            return 0;
+        }
+
+        if (sizeof(no_move = obs & all_inventory(dest))) {
+            TME(capitalize(CountUp(map_objects(no_move, "name", WER, 1))) +
+                (sizeof(no_move) == 1 ? " ist" : " sind") +
+                " doch bereits in " + dest->name(WEM,1) + ".");
+            if (!sizeof(obs -= no_move))
+                return 0;
+        }
+
+        foreach (object o: obs) {
+            if (objectp(o))
+                put(o, dest, msg);
+
+            if (get_eval_cost() < 100000) {
+                TME("Den Rest laesst Du erst mal, wo er ist.");
+                last_moved_objects = obs[..member(obs, o)];
+                last_moved_where = dest;
+                return 1;
+            }
+        }
+
+        last_moved_objects = obs;
+        last_moved_where = dest;
+        return 1;
+    }
+    
+    return 0;
+}
+
+varargs int pick_objects(string str, int flag, mixed msg)
+{
+    object *obs;
+
+    if (((int)QueryProp(P_MAX_HANDS)) < 1){
+        NF("Ohne Haende kannst Du nichts nehmen.");
+        return 0;
+    }
+
+    if (!sizeof(obs = find_objects(str, 0, 1) - all_inventory()
+	  - (flag ? all_inventory(environment()) : ({}))))
+        return 0;
+
+    foreach (object o: obs) {
+        if (objectp(o))
+            pick(o, msg);
+
+        if (get_eval_cost() < 100000) {
+            TME("Den Rest laesst Du erst mal liegen.");
+            last_moved_objects = obs[..member(obs, o)];
+            last_moved_where = 0;
+            return 1;
+        }
+    }
+
+    last_moved_objects = obs;
+    last_moved_where = 0;
+    return 1;
+}
+
+varargs int give_objects(string str, mixed msg)
+{
+    object *obs, dest;
+    
+    if (!stringp(str) || !sizeof(str)) return 0;
+
+    string *tokens = explode(str, " ");
+
+    if (((int)QueryProp(P_MAX_HANDS)) < 1){
+        NF("Ohne Haende kannst Du nichts weggeben.");
+        return 0;
+    }
+
+    for (int i = 0; i < sizeof(tokens)-1; i++) {
+        if (!(dest = present(implode(tokens[..i], " "), environment())))
+            continue;
+
+        if (!living(dest)) {
+            NF("Aber " + dest->name(WER, 1) + " lebt doch gar nicht!");
+            dest = 0;
+            continue;
+        }
+
+        if (!sizeof(obs = __find_objects(tokens[i+1..], 0, 1))) {
+            NF("WAS moechtest Du " + dest->name(WEM, 1)+" geben?");
+            dest = 0;
+        } else
+            break;
+    }
+
+    if (!dest) {
+        int pos;
+
+        if ((pos = strrstr(str, " an ")) >= 0) {
+            dest = present(str[pos+4..], environment());
+            // zu gebende Objekte in Env + Living suchen
+            obs = find_objects(str[..pos-1], 0, 1);
+        }
+    }
+
+    if (!dest || !living(dest) || !sizeof(obs))
+        return 0;
+
+    foreach (object o: obs) {
+        if (objectp(o))
+            give(o, dest, msg);
+
+        if (get_eval_cost() < 100000) {
+            TME("Den Rest behaeltst Du erst mal.");
+            last_moved_objects = obs[..member(obs, o)];
+            last_moved_where = dest;
+            return 1;
+        }
+    }
+
+    last_moved_objects = obs;
+    last_moved_where = dest;
+    return 1;
+}
+
+varargs int show_objects(string str, mixed msg)
+{
+    object *obs, whom;
+    
+    if (!stringp(str) || !sizeof(str))
+      return 0;
+
+    string *tokens = explode(str, " ");
+
+    for (int i = 0; i < sizeof(tokens)-1; i++) {
+        if (whom = present(implode(tokens[..i], " "), environment())) {
+            if (!living(whom)) {
+                NF("Aber " + whom->name(WER, 1) + " lebt doch gar nicht!");
+                continue;
+            }
+        } else {
+            if (i != 0 || tokens[0] != "allen")
+                continue;
+
+            if (!sizeof(filter(all_inventory(environment()) -
+                    ({ this_object() }), #'living))) {
+                NF("Hier ist niemand, dem Du etwas zeigen koenntest!");
+                continue;
+            }
+        }
+
+        if (whom == this_object()) {
+            NF("Dazu solltest Du dann besser 'schau' benutzen!\n");
+            continue;
+        }
+
+        if (!sizeof(obs = __find_objects(tokens[i+1..], this_object(), 0)) &&
+            !sizeof(obs = __find_objects(tokens[i+1..], 0, 0)
+                            - ({ this_object(), whom }))) {
+            NF("WAS moechtest Du " + (whom ? whom->name(WEM, 1) : "allen") +
+               " zeigen?");
+            continue;
+        }
+
+        foreach (object o: obs) {
+            if (objectp(o))
+                show(o, whom, msg);
+
+            if (get_eval_cost() < 100000) {
+                TME("Das reicht erst mal.");
+                last_moved_objects = obs[..member(obs, o)];
+                last_moved_where = whom;
+                return 1;
+            }
+        }
+
+        last_moved_objects = obs;
+        last_moved_where = whom;
+        return 1;
+    }
+
+    return 0;
+}
+
+object *moved_objects(void)
+{
+    return last_moved_objects;
+}
+
+object moved_where(void)
+{
+    return last_moved_where;
+}
+
+
+/************************* Die einzelnen Kommandos **************************/
+
+/* static int fallenlassen(string str)
+ * static int werfen(string str)
+ * static int legen(string str)
+ * static int stecken(string str)
+ * static int holen(string str)
+ * static int nehmen(string str)
+ * static int geben(string str)
+ *   Minimale Wrapper fuer XXX_objects(), entfernen "fallen", "weg" bzw. "ab"
+ *   aus den Argumenten und setzen entsprechende Standard-Fehlermeldungen.
+ *   
+ * protected void add_put_and_get_commands()
+ *   Registriert obige Funktionen per add_action().
+ */
+
+static int fallenlassen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts fallenlassen.\n");
+        return 0;
+    }
+
+    if (!str || str[<7..] != " fallen") {
+        _notify_fail("Lass etwas FALLEN, oder was meinst Du?\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du fallenlassen?\n");
+    return drop_objects(str[0..<8]);
+}
+
+static int werfen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts wegwerfen.\n");
+        return 0;
+    }
+
+    if (!str || str[<4..] != " weg") {
+        _notify_fail("Wirf etwas WEG, oder was meinst Du?\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du loswerden?\n");
+    return drop_objects(str[0..<5]);
+}
+
+static int legen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts weglegen.\n");
+        return 0;
+    }
+
+    if (!str) {
+        _notify_fail("Lege etwas AB, oder was meinst Du?\n");
+        return 0;
+    }
+
+    if (str[<3..] == " ab") {
+        _notify_fail("WAS moechtest Du ablegen?\n");
+        return drop_objects(str[0..<4]);
+    }
+
+    if (str[<4..] == " weg") {
+        _notify_fail("WAS moechtest Du weglegen?\n");
+        return drop_objects(str[0..<5]);
+    }
+
+    _notify_fail("WAS moechtest Du WOHIN legen?\n");
+    return put_objects(str, WEN, "legen");
+}
+
+static int stecken(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Das kannst Du in Deinem immateriellen Zustand nicht.\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du WOHIN stecken?\n");
+    return put_objects(str, WEN, "stecken");
+}
+
+static int holen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts nehmen.\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du aus WAS holen?\n");
+    return pick_objects(str, 1);
+}
+
+static int nehmen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts nehmen.\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du nehmen?\n");
+    return pick_objects(str, 0);
+}
+
+static int geben(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts weggeben.\n");
+        return 0;
+    }
+
+    _notify_fail("WEM moechtest Du WAS geben?\n");
+    return give_objects(str);
+}
+
+static int zeigen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du niemandem etwas zeigen.\n");
+        return 0;
+    }
+
+    _notify_fail("WEM moechtest Du WAS zeigen?\n");
+    return show_objects(str);
+}
+
+protected void add_put_and_get_commands(void)
+{
+    add_action("fallenlassen", "lass");
+    add_action("fallenlassen", "lasse");
+    add_action("werfen",       "wirf");
+    add_action("werfen",       "werf");
+    add_action("werfen",       "werfe");
+    add_action("legen",        "leg");
+    add_action("legen",        "lege");
+    add_action("stecken",      "steck");
+    add_action("stecken",      "stecke");
+    add_action("holen",        "hol");
+    add_action("holen",        "hole");
+    add_action("nehmen",       "nimm");
+    add_action("nehmen",       "nehm");
+    add_action("nehmen",       "nehme");
+    add_action("geben",        "gebe");
+    add_action("geben",        "gib");
+    add_action("zeigen",       "zeig");
+    add_action("zeigen",       "zeige");
+}
+
+
+/********** Aus reinen Kompatibilitaetsgruenden weiterhin enthalten *********/
+
+object* find_obs(string str, int meth)
+// gibt ein array zurueck mit allen Objekten die mit str angesprochen werden
+{
+   object inv;
+   if (!str) return 0;
+   if (str[<7..]==" in mir") {
+     inv=ME;
+     str=str[0..<8];
+   }
+   else if (str[<8..]==" in raum") {
+     if (meth & PUT_GET_DROP) { // man kann nichts aus dem Raum wegwerfen
+       _notify_fail("Du kannst nichts wegwerfen, das Du gar nicht hast.\n");
+       return 0;
+     }
+     inv=environment();
+     str=str[0..<9];
+   }
+   else if (meth & PUT_GET_DROP) inv=ME; // Raum bei drop uninteressant
+   // else kein besonderes inv ausgewaehlt also inv=0
+   if (!sizeof(str))
+     return 0; // hier passt die bereits gesetzte _notify_fail
+   else {
+     object *obs;
+     string con;
+     if (sscanf(str, "%s aus %s", str, con)==2 ||
+         sscanf(str, "%s in %s", str, con)==2 ||
+         sscanf(str, "%s von %s", str, con)==2 ||
+         sscanf(str, "%s vom %s", str, con)==2) {
+       if (!inv) {
+         if (!environment() || !(inv=present(con, environment())))
+            inv=present(con, ME); // sowohl im env als auch im inv suchen
+       }
+       else inv=present(con, inv); // nur in ausgewaehltem inv suchen
+       if (inv==ME) inv=0;
+       if (!inv || !(inv->short())) {
+         _notify_fail(break_string("Du hast hier aber kein '"+capitalize(con)
+                                 +"'.",78));
+         return 0;
+       }
+       if (living(inv)) {
+         _notify_fail(break_string("Aber "+inv->name(WER,1)+" lebt doch!",78));
+         return 0;
+       }
+       // wieso man aus Objekten die von std/tray abgeleitet werden etwas
+       // nehmen koennen soll, versteh ich zwar nicht so ganz...
+       if (!(inv->QueryProp(P_CONTAINER)) && !(inv->QueryProp(P_TRAY))) {
+         _notify_fail(break_string("Du kannst nichts aus "+inv->name(WEM,1)
+                                 +" nehmen.",78));
+         return 0;
+       }
+       if (inv->QueryProp(P_CNT_STATUS)) { // Container ist geschlossen
+         _notify_fail(break_string("Aber "+inv->name(WER,1)
+                                 +" ist doch geschlossen.", 78));
+         return 0;
+       }
+     }
+     else if (inv==ME && (meth & PUT_GET_TAKE)) { // nichts aus sich nehmen
+       _notify_fail("Du kannst nichts nehmen, "
+                    "was Du schon bei Dir traegst.\n");
+       return 0;
+     }
+     if (!inv && (meth & PUT_GET_TAKE))
+       inv=environment(); // nichts nehmen was man schon hat
+
+     if (!inv) {
+       if (environment()) {
+         obs=(environment()->present_objects(str)||({}));
+         if (!sizeof(obs)) obs+=(ME->present_objects(str)||({}));
+       }
+       else obs=(ME->present_objects(str) || ({}));
+     }
+     else obs=(inv->present_objects(str) || ({}));
+     return obs-({ ME });
+   }
+   return(0);
+}
+
+int pick_obj(object ob)
+{
+  object env;
+
+  if (!ob || ob == this_object() || environment(ob) == this_object()) return 0;
+  if ((env=environment(ob)) != environment()) {
+    if (!env->QueryProp(P_CONTAINER) && !env->QueryProp(P_TRAY)) {
+      TME("Du kannst nichts aus " + env->name(WEM,1) + " nehmen.");
+      return 1;
+    }
+    else if (env->QueryProp(P_CNT_STATUS)) {  // Container ist geschlossen
+      TME("Aber " + env->name(WER, 1) + " ist doch geschlossen.");
+      return 1;
+    }
+  }
+  if (ob->IsUnit() && ob->QueryProp(P_AMOUNT)<0) {
+    TME("Du kannst nicht mehr nehmen als da ist.");
+    return 1;
+  }
+  pick(ob);
+  return 1;
+}
+
+int drop_obj(object ob)
+{
+  if (!ob || ob==this_object() || environment(ob)!=this_object()) return 0;
+  drop(ob);
+  return 1;
+}
+
+int put_obj(object ob, object where)
+{
+  object env;
+
+  if (ob == this_object() || ob == where || environment(ob) == where) return 0;
+  env=environment(ob);
+  if (!where->QueryProp(P_CONTAINER)) {
+    TME("Du kannst in " + where->name(WEN,1) + " nix reinstecken.");
+    return 1;
+  }
+  if (where->QueryProp(P_CNT_STATUS)) {  // Container ist geschlossen
+    TME("Aber " + where->name(WER, 1) + " ist doch geschlossen.");
+    return 1;
+  }
+  if (env!=environment(this_object()) && env!=this_object()) {
+    _notify_fail("Da kommst du so nicht ran.\n");
+    return 0;
+  }
+  put(ob, where);
+  return 1;
+}
+
+int give_obj(object ob, object where)
+{
+  object env;
+
+  if (environment(ob)!=this_object()) {
+    TME("Das solltest Du erstmal nehmen.");
+    return 1;
+  }
+  if (!ob || ob == this_object() || ob == where ||
+      environment(where)!=environment())
+    return 0;
+  if (environment(ob) == where) {
+    _notify_fail("Das Ziel ist in dem zu gebenden Object enthalten!\n");
+    return 0;
+  }
+  if (environment(ob)!=this_object()) {
+    TME("Das hast Du nicht.");
+    return 1;
+  }
+  give(ob, where);
+  return 1;
+}
diff --git a/std/living/skill_attributes.c b/std/living/skill_attributes.c
new file mode 100644
index 0000000..ad90aa0
--- /dev/null
+++ b/std/living/skill_attributes.c
@@ -0,0 +1,379 @@
+// MorgenGrauen MUDlib
+//
+// living/skills_attributes.c - Verwaltung der Skillattribute von Lebewesen
+//
+// $Id: skills.c 6673 2008-01-05 20:57:43Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/util/executer";
+
+#define NEED_PROTOTYPES
+#include <living/skill_attributes.h>
+#include <player/life.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <properties.h>
+#include <defines.h>
+
+//#define ZDEBUG(x) if (find_player("zesstra")) tell_object(find_player("zesstra"),x)
+#define ZDEBUG(x)
+//#define SASETLOG(x) log_file("SASET.LOG", x, 250000)
+
+//#define __DEBUG__
+
+// Variable fuer die Skill-Attribute, Datenstruktur:
+/*  ([ SA_ATTR: ({Summe_Stat_Modifier, Zeitpunkt, AnzahlModifier, });
+              ([ ob1:value;duration,
+                 ob2:value;duration, ...]);  // stat. Modifier
+              ([ ob1:closure;duration,
+                 ob2:closure;duration, ...])     // dyn. Modifier
+             ,
+     SA_ATTR2: ({...}); ([]); ([]),
+   ]) */
+private nosave mapping skillattrs;
+
+protected void create() {
+  Set(P_SKILL_ATTRIBUTES, SECURED, F_MODE_AS);
+}
+
+// von aussen Prop setzen ist nicht...
+mapping _set_skill_attr(mapping sa) {
+  return deep_copy(skillattrs);
+}
+// und auch beim Abfragen nur kopien liefern. ;-)
+mapping _query_skill_attr() {
+  return deep_copy(skillattrs);
+
+//TODO: Evtl. ext. Setzen von P_SKILL_ATTRIBUTE_OFFSETS mitloggen?
+}
+
+private void UpdateSACache(string *attrs) {
+#ifdef __DEBUG__
+    if (getuid(this_object()) == "zesstra")
+    ZDEBUG(sprintf("%O\n",skillattrs));
+#endif
+  if (!mappingp(skillattrs)) return;
+  if (!pointerp(attrs) || sizeof(attrs))
+    attrs = m_indices(skillattrs); // alle
+  // sonst schnittmenge aus existierenden und den uebergebenen.
+  attrs = m_indices(skillattrs) & attrs;
+
+  // und jetzt ueber alle gewuenschten SAs drueber
+  foreach(string attr : attrs) {
+    int *cache = skillattrs[attr, SAM_CACHE];
+    mapping stat = skillattrs[attr, SAM_STATIC];
+    mapping dyn = skillattrs[attr, SAM_DYNAMIC];
+    int sum = 0;
+    int timeout = __INT_MAX__;
+ 
+    // ueber stat. Mods iterieren und Aufsummieren, kleinsten Timeout
+    // ermitteln.
+    foreach(object ob, int value, int duration: stat) {
+      // gueltige Mods aufaddieren, abgelaufene rauswerfen
+      if (duration >= time()) {
+        sum += value;
+        if (duration < timeout)
+          timeout = duration;
+      }
+      else
+        m_delete(stat, ob); // ja, geht im foreach ;-)
+    }
+    // Ablaufzeiten der dyn. Mods pruefen, waere hier zwar nicht unbedingt
+    // noetig sondern koennte man ausschliesslich im QuerySkillAttribute()
+    // machen, aber dann waere die Ermittlung der Summe der Mods schwieriger.
+    foreach(object ob, closure value, int duration: dyn) {
+      if (duration < time())
+        m_delete(dyn, ob); // ungueltig, weg damit.
+    }
+    // gesamtzahl Mods?
+    cache[SAM_COUNT] = sizeof(stat) + sizeof(dyn);
+    if (!cache[SAM_COUNT]) {
+      // keine mods da, Submapping fuer dieses SA komplett loeschen.
+      m_delete(skillattrs, attr);
+      continue;
+    }
+    // sonst die anderen Cache-Werte setzen.
+    cache[SAM_SUM] = sum;
+    cache[SAM_CACHE_TIMEOUT] = timeout;
+  }
+  // wenn alle Mods geloescht wurden.
+  if (!sizeof(skillattrs)) skillattrs=0;
+#ifdef __DEBUG__
+  if (getuid(this_object()) == "zesstra")
+    ZDEBUG(sprintf("%O\n",skillattrs));
+#endif
+}
+
+private int InternalModifySkillAttribute(object caster, string atrname,
+                                mixed value, int duration) {
+  int zeit = utime()[1];
+  int ticks = get_eval_cost();
+
+  // nur existierende SAs...
+  if (!stringp(atrname)
+      || member(VALID_SKILL_ATTRIBUTES, atrname) == -1)
+    return SA_MOD_INVALID_ATTR;
+
+  if (!objectp(caster)) return SA_MOD_INVALID_OBJECT;
+
+  if (!mappingp(skillattrs)) skillattrs=m_allocate(1,3);
+  
+  if (!member(skillattrs, atrname)) {
+    skillattrs[atrname,SAM_CACHE] = ({0, 0, 0});
+    // die meisten Mods sind statisch, daher auf Verdacht hier fuer einen
+    // Eintrag Platz reservieren, aber nicht fuer den dyn. Teil.
+    skillattrs[atrname,SAM_STATIC] = m_allocate(1,2);
+    skillattrs[atrname,SAM_DYNAMIC] = m_allocate(0,2);
+  }
+  // pruefen, ob Maximalzahl an Eintraegen drin ist
+  else if (skillattrs[atrname,SAM_CACHE][SAM_COUNT] >= SAM_MAX_MODS) {
+    // letzte Chance: destructete Objekte drin?
+    skillattrs[atrname,SAM_CACHE][SAM_COUNT] = 
+      sizeof(skillattrs[atrname,SAM_STATIC])
+    +sizeof(skillattrs[atrname,SAM_DYNAMIC]);
+    // es kann sein, dass noch abgelaufene Objekte drinstehen,
+    // aber die Pruefung ist mir gerade zu teuer. TODO
+    // nochmal gucken (rest vom cache wird ggf. unten geprueft.)
+    if (skillattrs[atrname,SAM_CACHE][SAM_COUNT] >= SAM_MAX_MODS)
+      return SA_TOO_MANY_MODS; // dann nicht.
+  }
+
+  // Dauer darf nur ein int sein.
+  if (!intp(duration))
+    raise_error(sprintf("Wrong argument 3 to ModifySkillAttribute: "
+    "expected 'int', got %.10O\n", duration));
+  // Zeitstempel ermitteln
+  duration += time();
+
+  // statischer oder dyn. Modifier?
+  if (intp(value)) {
+    // Mod darf nicht zu gross oder zu klein sein. TODO: Grenzen?
+    if (value < -1000)
+      return SA_MOD_TOO_SMALL;
+    else if (value > 1000)
+      return SA_MOD_TOO_BIG;
+    else if (!value)
+      return SA_MOD_INVALID_VALUE; 
+    // jedes Objekt darf nur einen mod haben. Wenn dieses schon einen dyn.
+    // hat, muss der geloescht werden (stat. werden ja eh ersetzt).
+    if (member(skillattrs[atrname,SAM_DYNAMIC], caster))
+      m_delete(skillattrs[atrname,SAM_DYNAMIC], caster);
+    // sonst eintragen
+    skillattrs[atrname, SAM_STATIC] += ([caster: value; duration]);
+  }
+  else if (closurep(value)) {
+    // nur ein Mod pro Objekt, s.o.
+    if (member(skillattrs[atrname,SAM_STATIC], caster))
+      m_delete(skillattrs[atrname,SAM_STATIC], caster);
+    // direkt ohne weitere Pruefung eintragen
+    skillattrs[atrname, SAM_DYNAMIC] += ([caster: value; duration]);
+  }
+  else
+    raise_error(sprintf("Wrong argument 2 to ModifySkillAttribute(): "
+    "expected 'int' or 'closure', got %.10O\n",value));
+
+#ifdef SASETLOG
+  if (query_once_interactive(this_object()))
+    SASETLOG(sprintf("%s: %O, %s, %O, %O\n", 
+        strftime("%y%m%d-%H%M%S"), this_object(), atrname, caster, value));
+#endif
+#ifdef SASTATD
+  object daemon;
+  if (query_once_interactive(ME)
+      && objectp(daemon=find_object(SASTATD)))
+    daemon->LogModifier(caster, atrname, value, duration);
+#endif
+  // noch den Cache fuer dieses SA neu berechnen
+  // TODO: Cache nur invalidieren, damit er erst bei der naechsten Abfrage
+  // aktualisiert wird. Spart Zeit, wenn bis dahin mehrere Mods
+  // entfernt/addiert werden. Dafuer ist die gecache Anzahl an Mods
+  // inkonsistent.
+  UpdateSACache( ({atrname}) );
+
+  ZDEBUG(sprintf("MSA: %O, Zeit: %d, Ticks: %d\n",
+	this_object(),
+	utime()[1]-zeit, ticks-get_eval_cost()));
+
+  return SA_MOD_OK;
+}
+
+public int ModifySkillAttribute(string atrname, mixed value, 
+                                    int duration) {
+  return InternalModifySkillAttribute(
+      (extern_call()?previous_object():ME), atrname, value, duration);
+}
+
+public int RemoveSkillAttributeModifier(object caster, string attrname) {
+  if (!stringp(attrname) || !mappingp(skillattrs) || !objectp(caster)
+      || !member(skillattrs, attrname))
+    return SA_MOD_NOT_FOUND;
+  // TODO: Berechtigung pruefen. ;-)
+
+  if (member(skillattrs[attrname, SAM_STATIC], caster)) {
+    m_delete(skillattrs[attrname, SAM_STATIC], caster);
+  }
+  else if (member(skillattrs[attrname, SAM_DYNAMIC], caster)) {
+    m_delete(skillattrs[attrname, SAM_DYNAMIC], caster);
+  }
+  else
+    return SA_MOD_NOT_FOUND;
+  
+  // TODO: Cache nur invalidieren, damit er erst bei der naechsten Abfrage
+  // aktualisiert wird. Spart Zeit, wenn bis dahin mehrere Mods
+  // entfernt/addiert werden. Dafuer ist die gecache Anzahl an Mods
+  // inkonsistent.
+  UpdateSACache( ({attrname}) );
+
+  return SA_MOD_REMOVED;
+}
+
+public int QuerySkillAttribute(string atrname)
+{
+  mixed offsets, attr;
+  int modsumme, qual, ret, cval;
+
+  if (!stringp(atrname)) // VALID_SKILL_ATTRIBUTES beruecksichtigen?
+    return 100;
+
+  // wenn nicht SA_QUALITY gefragt ist, erstmal jenes ermitteln, weil es den
+  // ersten Modifier auf alle anderen SAs darstellt. Sonst den Startwert fuer
+  // SA_QUALITY (100) modifiziert durch evtl. Todesfolgen ermitteln.
+  if ( atrname != SA_QUALITY )
+    qual = QuerySkillAttribute(SA_QUALITY);
+  else
+    // bei SA_QUALITY gehen die Todesfolgen ein
+    qual = to_int(100 * pow(0.9, death_suffering()/10.0));
+
+  // Die Offsets sind sozusagen der Basiswert der SAs. Als erstes verwursten,
+  // sofern vorhanden, nen int drinsteht und der offset != 0 ist.
+  if ( mappingp(offsets = Query(P_SKILL_ATTRIBUTE_OFFSETS))
+       && intp(attr=offsets[atrname]) 
+       && attr)
+    ret = attr;
+  else
+    ret = 100;
+
+  // wenn keine Mods gesetzt sind, wars das jetzt. ;-)
+  if ( !mappingp(skillattrs)
+       || !member(skillattrs, atrname) )
+    return (ret*qual)/100;
+
+  // wenn Cache der stat. Mods abgelaufen oder offenbar Objekte zerstoert
+  // wurden, muss der Cache neu berechnet werden.
+  if ( skillattrs[atrname,SAM_CACHE][SAM_CACHE_TIMEOUT] < time()
+      || sizeof(skillattrs[atrname,SAM_STATIC])
+         +sizeof(skillattrs[atrname,SAM_DYNAMIC]) !=
+           skillattrs[atrname,SAM_CACHE][SAM_COUNT] )
+  {
+    UpdateSACache( ({atrname}) );
+    // UpdateSACache() loescht uU das SA-Mapping oder Eintraege daraus.
+    if ( !mappingp(skillattrs)
+         || !member(skillattrs, atrname) )
+    return (ret*qual)/100;
+  }
+  // Summe der statischen Mods.
+  modsumme = skillattrs[atrname,SAM_CACHE][SAM_SUM];
+
+  // TODO! Evtl. andere Daten als ME an die Funktion uebergeben
+  // TODO! bereits nach Addition des Funktionsrueckgabewertes pruefen, ob der
+  //       Wertebereich des SA-Modifiers ueberschritten ist, oder freien
+  //       Wertebereich erlauben und erst am Ende deckeln (aktuelle Variante)
+  // Dynamische Modifier auswerten
+  foreach( object ob, closure cl, int duration:
+           skillattrs[atrname,SAM_DYNAMIC] )
+  {
+    if ( duration > time()               // Noch nicht abgelaufen und
+         && intp(cval=funcall(cl, ME)) ) // Funktion liefert int zurueck
+      modsumme += cval;
+    else {
+      m_delete(skillattrs[atrname,SAM_DYNAMIC], ob);
+      skillattrs[atrname,SAM_CACHE][SAM_COUNT]--;
+    }
+  }
+  ret = ((ret+modsumme)*qual)/100;
+  if ( ret < 10 )
+    ret = 10;
+  else if ( ret > 1000 )
+    ret = 1000;
+
+  return ret;
+}
+
+public varargs mapping QuerySkillAttributeModifier(object caster, 
+                           string *attrnames) {
+  
+  // auf abgelaufene Modifikatoren pruefen
+  if (!pointerp(attrnames))
+    UpdateSACache( ({}) );
+  else
+    UpdateSACache(attrnames);
+
+  if (!mappingp(skillattrs))
+    return ([]);
+  if (!pointerp(attrnames) || !sizeof(attrnames))
+    attrnames = m_indices(skillattrs); // alle durchsuchen
+  else // schnittmenge der gew. und vorhandenen bilden
+    attrnames = m_indices(skillattrs) & attrnames;
+  
+  mapping res=m_allocate(sizeof(attrnames), 1);
+
+  foreach(string atr: attrnames) {
+    res[atr] = m_allocate(5, 2); // mal fuer 5 Werte Platz reservieren
+    if (!objectp(caster)) {
+      // wenn kein bestimmter caster angefragt ist, alle mods liefern
+      foreach(object c, int value, int dur: skillattrs[atr, SAM_STATIC]) {
+	res[atr] += ([c: value; dur]);
+      }
+      foreach(object c, closure value, int dur: skillattrs[atr, SAM_DYNAMIC]) {
+	res[atr] += ([c: value; dur]);
+      }
+    }
+    else {
+      // sonst nur den Mod von caster
+      if (member(skillattrs[atr, SAM_STATIC], caster)) {
+	res[atr] += ([caster: 
+			 skillattrs[atr, SAM_STATIC][caster, SAM_VALUE];
+	                 skillattrs[atr, SAM_STATIC][caster, SAM_DURATION]
+		    ]);
+      }
+      else if (member(skillattrs[atr, SAM_DYNAMIC], caster)) {
+	res[atr] += ([caster:
+			 skillattrs[atr, SAM_DYNAMIC][caster, SAM_VALUE];
+	                 skillattrs[atr, SAM_DYNAMIC][caster, SAM_DURATION]
+		    ]);
+      }
+    }
+  }
+  return res;
+}
+
+// Kompatibilitaetsfunktion mit altem Interface. Ist nur ein Wrapper, der
+// value umrechnet und 'alte' Rueckgabewerte liefert.
+
+public varargs int ModifySkillAttributeOld(object caster, string atrname,
+                          int value, int duration, mixed fun) {
+  int res;
+  // Caller ermitteln
+  if (extern_call()) caster=previous_object();
+  else caster=ME;
+
+  // Closures koennen via ModifySkillAttributeOld() nicht mehr gesetzt werden,
+  // da deren Rueckgabewert nicht sinnvoll umgerechnet werden koennen. (Man
+  // weiss nicht, ob es eine neue oder alte Closure ist.)
+  if (pointerp(fun) || closurep(fun))
+      raise_error(sprintf("Closures for SA modifiers can't be set by "
+      "ModifySkillAttributeOld()! Use ModifySkillAttribute()!\n"));
+  
+  res = InternalModifySkillAttribute(caster, atrname, value-100, duration);
+  // die alte funktion hatte nur 0 fuer ungueltigen Wert und < 0 fuer zu
+  // kleines Level als Rueckgabewert. Zu kleines Level gibt nicht mehr, also
+  // bleibt nur 0 als Sammel-Fehlercode uebrig. *seufz*
+  if (res < 0) return 0;
+  return res;
+}
+
diff --git a/std/living/skill_utils.c b/std/living/skill_utils.c
new file mode 100644
index 0000000..24caf75
--- /dev/null
+++ b/std/living/skill_utils.c
@@ -0,0 +1,35 @@
+// MorgenGrauen MUDlib
+//
+// living/skill_utils -- some helper functions for manipulating skill data 
+//                       needed in more than one program.
+//
+// $Id: skill_utils.c 6738 2008-02-19 18:46:14Z Humni $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <new_skills.h>
+
+protected void SkillResTransfer(mapping from_M, mapping to_M)
+{
+  if ( !mappingp(from_M) || !mappingp(to_M) )
+    return;
+
+  if ( member(from_M,SI_SKILLDAMAGE) )
+    to_M[SI_SKILLDAMAGE] = to_int(from_M[SI_SKILLDAMAGE]);
+
+  if ( member(from_M,SI_SKILLDAMAGE_MSG) )
+    to_M[SI_SKILLDAMAGE_MSG] = to_string(from_M[SI_SKILLDAMAGE_MSG]);
+
+  if ( member(from_M,SI_SKILLDAMAGE_MSG2) )
+    to_M[SI_SKILLDAMAGE_MSG2] = to_string(from_M[SI_SKILLDAMAGE_MSG2]);
+
+  if ( member(from_M,SI_SKILLDAMAGE_TYPE) )
+    to_M[SI_SKILLDAMAGE_TYPE] = from_M[SI_SKILLDAMAGE_TYPE];
+
+  if ( member(from_M,SI_SPELL) )
+    to_M[SI_SPELL] = from_M[SI_SPELL];
+}
+
diff --git a/std/living/skills.c b/std/living/skills.c
new file mode 100644
index 0000000..93c9dcc
--- /dev/null
+++ b/std/living/skills.c
@@ -0,0 +1,595 @@
+// MorgenGrauen MUDlib
+//
+// living/skills.c -- Gilden-, Skill- und Spellfunktionen fuer Lebewesen
+//
+// $Id: skills.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/std_skills";
+
+#define NEED_PROTOTYPES
+#include <living/skills.h>
+#include <living/skill_attributes.h>
+#include <player/life.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <new_skills.h>
+#include <wizlevels.h>
+
+// speichert die Spell-Fatigues (global, Spruchgruppen, Einzelsprueche)
+private mapping spell_fatigues = ([]);
+
+// Prototypen
+private void expire_spell_fatigues();
+
+protected void create()
+{
+  // mainly necessary for players, but there may be some NPC with savefiles.
+  // Additionally, it simplifies expiration of old keys to have it here.
+  call_out(#'expire_spell_fatigues, 4);
+}
+
+
+// Diese - hier scheinbar sinnlose - Funktion wird von /std/player/skills.c dann 
+// ueberladen.
+public int is_deactivated_skill(string sname, string gilde)
+{
+	return 0;
+}
+
+
+
+static string _query_visible_guild()
+{ string res;
+
+  if ( stringp(res=Query(P_VISIBLE_GUILD)) )
+    return res;
+
+  return QueryProp(P_GUILD);
+}
+
+static string _query_visible_subguild_title()
+{ string res;
+
+  if ( stringp(res=Query(P_VISIBLE_SUBGUILD_TITLE)) )
+    return res;
+
+  return QueryProp(P_SUBGUILD_TITLE);
+}
+
+static mixed _query_guild_prepareblock()
+{ mapping res;
+  string  gilde;
+
+  if ( !stringp(gilde=QueryProp(P_GUILD)) )
+    return 0;
+  if ( !mappingp(res=Query(P_GUILD_PREPAREBLOCK)) 
+      || !member(res,gilde) )
+    return 0;
+  return res[gilde];
+}
+
+static mixed _set_guild_prepareblock(mixed arg)
+{ mapping res;
+  string  gilde;
+
+  if ( !stringp(gilde=QueryProp(P_GUILD)) )
+    return 0;
+  if ( !mappingp(res=Query(P_GUILD_PREPAREBLOCK)) )
+    res=([]);
+
+  res[gilde]=arg;
+  Set(P_GUILD_PREPAREBLOCK,res);
+
+  return arg;
+}
+
+
+private nosave int valid_setskills_override;
+// Man sollte eigentlich ja nicht Parameter als globale Variablen
+// uebergeben, aber hier ging es nicht anders
+nomask private int valid_setskills(string gilde)
+{ string fn;
+
+  if ( !query_once_interactive(this_object()) )
+    return 1; // Monster duerfen sich selber Skills setzen :)
+
+  if ( QueryProp(P_TESTPLAYER) || IS_WIZARD(this_object()) )
+      return 1; // Testspieler und Magier sind schutzlose Opfer ;-)
+
+  if ( previous_object() )
+  {
+    if ( previous_object()==this_object()
+        && this_interactive()==this_object() )
+      return 1;
+
+    fn=object_name(previous_object());
+    if ( fn[0..7]=="/gilden/"
+        || fn[0..11]=="/spellbooks/"
+        || fn[0..7]=="/secure/"
+        || fn[0..11]=="/p/zauberer/" )
+      return 1; // Die sollten problemlos aendern duerfen
+
+    if ( file_size("/gilden/access_rights")>0
+       && call_other("/gilden/access_rights",
+                    "access_rights",
+                    getuid(previous_object()),
+                    gilde+".c"))
+      return 1; // Setzendes Objekt kommt vom Gildenprogrammierer
+
+    if ( file_size("/gilden/"+gilde+".c")>0
+        && call_other("/gilden/"+gilde,
+                      "valid_setskills",
+                        explode(fn,"#")[0]) )
+      return 1; // Die Gilde selber kann Ausnahmen zulassen
+  }
+
+  if (valid_setskills_override)
+  {
+    valid_setskills_override=0;
+    return 1; // Fuers Setzen der Closure
+  }
+
+  if ( this_interactive() )
+  {
+    if ( IS_ARCH(this_interactive()) )
+      return 1; // Erzmagier duerfen immer aendern
+
+    if ( call_other("/gilden/access_rights",
+                    "access_rights",
+                    getuid(this_interactive()),
+                    gilde+".c"))
+      return 1;  // Der Gildenprogrammierer selber auch
+  }
+
+  // Fuer die Waffenskills, die sollen sich selbst auch setzen duerfen
+  if (!this_interactive() && this_object()==previous_object())
+	  return 1;
+  
+  
+  log_file("SETSKILLS",sprintf("*****\n%s PO:%O->TO:%O TI:%O\n GUILD:%s VERB_ARGS:'%s'\n",
+            ctime(time())[4..15],
+            previous_object(),
+            this_object(),
+            this_interactive(),
+            gilde,
+            ( this_interactive() ? query_verb() + " " +
+                this_interactive()->_unparsed_args() : "") ));
+
+  return 0;
+}
+
+// Nur interne Verwendung, value wird nicht weiter prueft, muss ok sein.
+// Es wird keine Kopie von value gemacht, wenn es ins Mapping geschrieben
+// wird!
+private mapping internal_set_newskills(mapping value, string gilde) {
+  mapping skills;
+
+  // in der richtigen Gilde setzen.
+  if ( !gilde && !(gilde=QueryProp(P_GUILD)) )
+    gilde="ANY";
+
+  // Query(), hier ist eine Kopie nicht sinnvoll.
+  if ( !mappingp(skills=Query(P_NEWSKILLS,F_VALUE)) ) {
+    skills=([]);
+    Set(P_NEWSKILLS, skills, F_VALUE);
+  }
+
+  // Falls dies hier mal ausgewertet werden sollte, nicht vergessen, dass
+  // einige Funktion hier im File die Prop evtl. via
+  // internal_query_newskills() abrufen und direkt aendern...
+  valid_setskills(gilde); // Sicherheitsueberpruefung
+  
+  // Skills setzen. Set() unnoetig, weil wir das von Query() gelieferte
+  // Mapping aendern und das ja via Referenz bekommen haben.
+  skills[gilde]=value;
+  //Set(P_NEWSKILLS,skills);
+
+  return(value);
+}
+
+// nur zur internen Verwendung, es wird keine Kopie des Skillmappings gemacht!
+private mapping internal_query_newskills(string gilde) {
+  mapping skills;
+
+  // richtige Gilde abfragen.
+  if ( !gilde && !(gilde=QueryProp(P_GUILD)) )
+    gilde="ANY";
+
+  skills=Query(P_NEWSKILLS);
+
+  if (!mappingp(skills) || !mappingp(skills=skills[gilde]) )
+    return ([]);
+
+  return(skills);
+}
+
+// Eigentlich sollte man den _query-Funktionen keine Parameter geben...
+static varargs mapping _query_newskills(string gilde) {
+
+  // sonst Kopie des spellmappings liefern! Kostet zwar, aber verhindert
+  // einige andere Bugs und versehentliche Aenderungen an den Skills!
+  return(deep_copy(internal_query_newskills(gilde)));
+}
+
+// Eigentlich sollte man den _set-Funktionen keine weiteren Parameter geben
+static varargs mapping _set_newskills(mapping value, string gilde) {
+
+  // value auf Mappings normalisieren, ggf. Kopieren
+  if ( !mappingp(value) )
+      value=([]);
+  else
+      //zur Sicherheit, wer weiss, was der setzende noch damit macht...
+      value=deep_copy(value);
+
+  // und setzen...
+  internal_set_newskills(value, gilde);
+
+  // und noch ne Kopie von dem Liefern, was wir gesetzt haben (keine Referenz,
+  // sonst koennte der Aufrufende ja noch im Nachhinein aendern).
+  return(_query_newskills(gilde));
+}
+
+private mapping InternalQuerySkill(string sname, string gilde) {
+  mixed skill, skills;
+  // In is_any wird gespeichert, ob es ein gildenunabhaengier Skill ist,
+  // fuer die is_deactivate_skill-Abfrage.
+  int is_any;
+
+  // Skills komplett abfragen, keine spez. Gilde
+  if (!mappingp(skills=Query(P_NEWSKILLS,F_VALUE)))
+      return 0;
+
+  if (stringp(gilde) && sizeof(gilde)) {
+    //bestimmte Gilde angegeben, gut, dort gucken.
+    if (mappingp(skills[gilde]))
+      skill=skills[gilde][sname]; 
+  }
+  else {
+    gilde=QueryProp(P_GUILD); //reale Gilde holen
+    if (gilde && mappingp(skills[gilde]) && 
+	(skill=skills[gilde][sname])) {
+      // gibt es den Spell in der Gilde des Spielers?
+      // dann hier nix machen...
+    }
+    else if (mappingp(skills["ANY"])) {
+     // Zum Schluss: Gibt es den Skill vielleicht Gildenunabhaengig?
+      skill=skills["ANY"][sname];
+      // wenn man hier reinkommt, dann spaeter mit is_deactivated_skill() 
+      // pruefen!
+      is_any=1;
+    }
+  }
+
+  // wenn kein Skill gefunden, mit 0 direkt raus
+  if (!skill) return 0;
+
+  // Bei gildenunabhaengigen auch im Skillmapping vermerken
+  if ( is_any ) {	
+      skill+=([SI_GUILD:"ANY"]);	
+      // Ist er vielleicht in der Gilde des Spielers deaktiviert? 		
+      // Dies kann nur der Fall sein, wenn es kein Gildenskill ist.		
+      if (is_deactivated_skill(sname,gilde)) {		    
+	  return 0;		
+      }
+  }
+
+  return(skill);
+}
+
+public varargs mapping QuerySkill(string sname, string gilde) {
+ 
+    if (!stringp(sname) || !sizeof(sname))
+	return 0;
+
+    //Kopie zurueckliefern
+    return(deep_copy(InternalQuerySkill(sname,gilde)));
+}
+
+#define SMUL(x,y) ((x<0 && y<0)?(-1*x*y):(x*y))
+public varargs int QuerySkillAbility(string sname, string gilde)
+{ mapping skill;
+  string skill2;
+
+  if ( !(skill=InternalQuerySkill(sname, gilde)) )
+    return 0;
+
+  int val=skill[SI_SKILLABILITY];
+
+  if (skill2=skill[SI_INHERIT])
+  {
+    int val2;
+    val2=QuerySkillAbility(skill2);
+    val=(val*MAX_ABILITY+SMUL(val,val2))/(2*MAX_ABILITY);
+  }
+
+  return val;
+}
+
+protected varargs mixed LimitAbility(mapping sinfo, int diff)
+{ mixed abil;
+  int max,old,d2;
+
+  abil=sinfo[SI_SKILLABILITY];
+
+  if ( !intp(abil) )
+    return sinfo;
+  old=abil;
+
+  // Beim Spieler eingetragene Schwierigkeit gilt vor angegebener.
+  if ( (d2=sinfo[SI_DIFFICULTY]) )
+    diff=d2;
+
+  // diff <-100 soll nicht hemmen und macht keinen Sinn
+  diff=(diff<(-100))?(-100):diff;
+  
+  max=MAX_ABILITY-(diff+100)*(35-QueryProp(P_LEVEL));
+
+// diff|lvl 1:|   3:|	7:| 10:| 13:| 16:| 19:| 22:| 25:| 28:| 31:| 34:|
+// ----+------+-----+-----+----+----+----+----+----+----+----+----+----+
+//  -50|   83%|  84%|  86%| 87%| 89%| 90%| 92%| 93%| 95%| 96%| 98%| 99%|
+//  -10|   69%|  72%|  74%| 77%| 80%| 82%| 85%| 88%| 91%| 93%| 96%| 99%|
+//    0|   66%|  69%|  72%| 75%| 78%| 81%| 84%| 87%| 90%| 93%| 96%| 99%|
+//   10|   62%|  65%|  69%| 72%| 75%| 79%| 82%| 85%| 89%| 92%| 95%| 98%|
+//   20|   59%|  62%|  66%| 70%| 73%| 77%| 80%| 84%| 88%| 91%| 95%| 98%|
+//   30|   55%|  59%|  63%| 67%| 71%| 75%| 79%| 83%| 87%| 90%| 94%| 98%|
+//   40|   52%|  56%|  60%| 65%| 69%| 73%| 77%| 81%| 86%| 90%| 94%| 98%|
+//   50|   49%|  53%|  58%| 62%| 67%| 71%| 76%| 80%| 85%| 89%| 94%| 98%|
+//  100|   32%|  38%|  44%| 50%| 56%| 62%| 68%| 74%| 80%| 86%| 92%| 98%|
+//  150|   15%|  22%|  30%| 37%| 45%| 52%| 60%| 67%| 75%| 82%| 90%| 97%|
+//  200|   -2%|   7%|  16%| 25%| 34%| 43%| 52%| 61%| 70%| 79%| 88%| 97%|
+//  250|  -19%|  -8%|	2%| 12%| 23%| 33%| 44%| 54%| 65%| 75%| 86%| 96%|
+//  300|  -36%| -24%| -12%|  0%| 12%| 24%| 36%| 48%| 60%| 72%| 84%| 96%|
+//  400|  -70%| -55%| -40%|-25%|-10%|  5%| 20%| 35%| 50%| 65%| 80%| 95%|
+//  500| -104%| -86%| -68%|-50%|-32%|-14%|  4%| 22%| 40%| 58%| 76%| 94%|
+//  600| -138%|-117%| -96%|-75%|-54%|-33%|-12%|  9%| 30%| 51%| 72%| 93%|
+
+  if ( abil>max )
+    abil=max;
+  if ( abil>MAX_ABILITY
+    ) abil=MAX_ABILITY;
+  else if ( abil<-MAX_ABILITY )
+    abil=-MAX_ABILITY;
+
+  if ( old && !abil )
+    abil=1;
+  // Faehigkeiten sollen nicht durch die Begrenzung verschwinden
+
+  sinfo[SI_SKILLABILITY]=abil;
+
+  return sinfo;
+}
+
+public varargs void ModifySkill(string sname, mixed val, int diff, string gilde)
+{ 
+  mapping skills;
+  mixed skill;
+
+  if ( !stringp(sname) || !sizeof(sname) )
+    return;
+
+  // internal_query_newskills() macht keine Kopie
+  skills=internal_query_newskills(gilde);
+
+  // Skill ermitteln, wenn nicht existiert, wird er angelegt.
+  if (!skill=skills[sname]) {
+      skill=([]); 
+  }
+  
+  // Zur Sicherheit mal das Mapping kopieren, wer weiss, was der	
+  // Aufrufende dieser Funktion selber spaeter damit noch macht.
+  // ist ok, wenn val kein Mapping ist, dann macht deep_copy nix.
+  val=deep_copy(val);
+
+  // Skill und val vereinigen
+  if ( mappingp(val) )
+    skill+=val;
+  else if (intp(val))
+    skill[SI_SKILLABILITY]=val;
+  else
+    raise_error(sprintf("Bad arg 2 to ModifySkill(): expected 'int', "
+          "got %.10O.\n",val));
+
+  // Lernen entsprechend SI_DIFFICULTY begrenzen.
+  if(diff && !member(skill,SI_DIFFICULTY))
+    skill[SI_DIFFICULTY]=diff;
+  skill=LimitAbility(skill,diff || skill[SI_DIFFICULTY]);
+  
+  // schliesslich im Skillmapping vermerken. Im Normalfall ist der Skill jetzt
+  // schon geaendert, nicht erst nach dem internal_set_newskills().
+  skills[sname]=skill;
+
+  // explizites Abspeichern fast ueberfluessig, weil wir oben eine Referenz
+  // auf das Skillmapping gekriegt haben...
+  // Aber es koennte sein, dass dies der erste Skill fuer diese Gilde ist,
+  // dann ist es noetig. Zum anderen wird internal_set_newskills() nochmal
+  // geloggt.
+  internal_set_newskills(skills,gilde);
+}
+
+public varargs void LearnSkill(string sname, int add, int diff)
+{ mapping skill;
+  string skill2,gilde;
+  int val;
+
+  // Spieler sollen nur lernen, wenn sie interactive sind. Das soll
+  // natuerlich nur fuer Spieler gelten.
+  if (query_once_interactive(this_object()) && !interactive())
+	  return;
+
+  if ( add>MAX_SKILLEARN )
+    add=MAX_SKILLEARN;
+  else if ( add<1 )
+    add=1;
+
+  // Skillmapping ermitteln (hier kommt keine Kopie zurueck)
+  skill=InternalQuerySkill(sname, 0);
+  // wenn kein Skill, dann Abbruch
+  if (!skill) return;
+
+  val=skill[SI_SKILLABILITY];
+  gilde=skill[SI_GUILD];
+ 
+  val+=add;
+
+  ModifySkill(sname,val,diff,gilde);
+  if ( skill2=skill[SI_INHERIT] )
+    LearnSkill(skill2,add/3,diff);
+}
+
+public varargs int UseSpell(string str, string spell)
+{ string gilde,sbook;
+  mapping sinfo;
+  closure cl;
+  
+  if ( !spell && !(spell=query_verb()) )
+    return 0;
+
+  spell=lower_case(spell);
+
+  // QuerySkill() liefert eine Kopie des Skillmappings.
+  // wenn skill unbekannt oder Ability <= 0, ist der Spell nicht nutzbar.
+  if ( !(sinfo=QuerySkill(spell,0))
+        || sinfo[SI_SKILLABILITY] <= 0 )
+    return 0;
+
+  sinfo[SI_SKILLARG]=str; // Argument eintragen
+
+  if ( !closurep(cl=sinfo[SI_CLOSURE]) )
+  {
+    // Wenn ein Spellbook angegeben ist wird der Spell direkt ausgefuehrt
+    if ( stringp(sbook=sinfo[SI_SPELLBOOK]) )
+      cl=symbol_function("UseSpell",SPELLBOOK_DIR+sbook);
+
+    // Wenn der Spieler in einer Gilde ist, so weiss diese, in welchem
+    // Spellbook der Spell zu finden ist...
+    else if ( (gilde=QueryProp(P_GUILD)) && 
+      ( find_object(GUILD_DIR+gilde) || file_size(GUILD_DIR+gilde+".c")>-1))
+      cl=symbol_function("UseSpell",GUILD_DIR+gilde);
+    else
+      cl=function int () {return 0;};
+
+    sinfo[SI_CLOSURE]=cl;
+    valid_setskills_override=1;
+    ModifySkill(spell,([SI_CLOSURE:cl]),0,sinfo[SI_GUILD]);
+    valid_setskills_override=0;
+  }
+  return funcall(cl,this_object(),spell,sinfo);
+}
+
+public varargs mixed UseSkill(string skill, mapping args)
+{ mapping sinfo;
+  string gilde, func,skill2;
+  mixed res;
+  closure cl;
+  
+  if ( !skill ||
+       QueryProp(P_GHOST))
+    return 0;
+
+  skill=capitalize(skill);
+  // QuerySkill() liefert eine Kopie des Skillmappings
+  if ( !(sinfo=QuerySkill(skill,0)) )
+    return 0;
+
+  if (args)
+    sinfo+=args;
+
+  if ( !closurep(cl=sinfo[SI_CLOSURE]) )
+  {
+    if ( !(func=sinfo[SI_SKILLFUNC])    // Keine Funktion angegeben?
+        || !(gilde=QueryProp(P_GUILD))) // Keine Gilde angegeben?
+    {
+      // Dann Standard-Funktion nehmen, wenn es die nicht gibt, den
+      // Ability-Wert zurueckliefern.
+      if (!closurep(cl = symbol_function("StdSkill_"+skill,this_object())))
+        cl=function int (object ob, string sname)
+             {return QuerySkillAbility(sname);} ;
+    }
+    else
+    {
+      // Sonst diese Funktion im Gildenobjekt aufrufen
+      cl=symbol_function(func,GUILD_DIR+gilde);
+    }
+
+    sinfo[SI_CLOSURE]=cl;
+    valid_setskills_override=1;
+    ModifySkill(skill,([SI_CLOSURE:cl]),0,sinfo[SI_GUILD]);
+    valid_setskills_override=0;
+  }
+
+  res=funcall(cl,this_object(),skill,sinfo);
+  if ( (skill2=sinfo[SI_INHERIT]) && mappingp(res) )
+    res=UseSkill(skill2,res); // Fuer Skills, die von anderen abhaengen
+
+  return res;
+}
+
+// ************** Spellfatigues ***************
+
+/*  Prueft die Spellfatigue fuer Spruch(gruppe) <key>.
+ *  <key> darf 0 sein (globale Spruchsperre).
+ *  Liefert 0, wenn keine Sperre und die Ablaufzeit, wenn eine Sperre noch
+ *  gueltig. ist.
+ */
+public varargs int CheckSpellFatigue(string key) {
+  // key==0 is the (default) global spellfatigue.
+  if (spell_fatigues[key] > time())
+    return spell_fatigues[key]; // Ablaufzeit zurueckgeben.
+
+  return 0; // ok, keine Sperre.
+}
+
+/** Speichert eine Spellfatigue von <duration> Sekunden fuer <key>.
+ * <key> darf 0 sein und bezeichnet das globale Spellfatigue.
+ * Rueckgabewert: Ablaufzeit der gesetzten Sperre
+                  -1, wenn noch eine nicht-abgelaufene Sperre auf dem <key> lag.
+                  0, wenn duration 0 ist.
+ */
+public varargs int SetSpellFatigue(int duration, string key) {
+  // aktuelle Sperre abgelaufen?
+  if (CheckSpellFatigue(key))
+    return -1; // alte Sperre noch aktiv.
+
+  duration += time();
+  // 0 is OK for <key>, it is the key for global spell fatigues
+  spell_fatigues[key] = duration;
+  return duration;
+}
+
+/*  Prueft die Spellfatigue fuer Spruch(gruppe) <key>.
+ *  <key> darf fuer diese Funktion 0 (globale Spruchsperre) sein, aber man
+ *  darf das Argument nicht weglassen, damit nicht ein verpeilter Magier
+ *  versehentlich die globale Spruchsperre nullt.
+ */
+public void DeleteSpellFatigue(string key) {
+  // key==0 is the (default) global spellfatigue.
+  m_delete(spell_fatigues, key);
+}
+
+/** Loescht abgelaufene Keys aus dem spell_fatigue mapping.
+ */
+private void expire_spell_fatigues() {
+  foreach(string key, int endtime: spell_fatigues) {
+    if (endtime <= time())
+      m_delete(spell_fatigues, key);
+  }
+}
+
+/** Setmethode fuer P_NEXT_SPELL_TIME.
+  */
+static int _set_next_spell(int fatigue) {
+  return SetSpellFatigue(fatigue - time());
+}
+/** Querymethode fuer P_NEXT_SPELL_TIME.
+  */
+static int _query_next_spell() {
+  return CheckSpellFatigue();
+}
+
diff --git a/std/living/std_skills.c b/std/living/std_skills.c
new file mode 100644
index 0000000..d132efc
--- /dev/null
+++ b/std/living/std_skills.c
@@ -0,0 +1,104 @@
+// MorgenGrauen MUDlib
+//
+// living/std_skills.c -- Standardfaehigkeiten
+//
+// $Id: std_skills.c 6673 2008-01-05 20:57:43Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <living/skills.h>
+#include <living/skill_attributes.h>
+#include <thing/properties.h>
+#include <attributes.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <new_skills.h>
+
+#define SIG(x) (x?(x>0?1:-1):0)
+
+protected int StdSkill_Nightvision(object me, string sname, mixed sinfo) {
+  int abil,light,llt,dt,res;
+
+  if (!sinfo || !environment()) return 0;
+  if (intp(sinfo)) sinfo=([SI_SKILLABILITY:sinfo]);
+  if (!mappingp(sinfo)) return 0;
+  if ((light=QueryProp(P_PLAYER_LIGHT))>0) {
+    ModifySkill(sname,([SI_LASTLIGHT:time()]));
+    return light;
+  }
+  abil=sinfo[SI_SKILLABILITY];
+  if (!(llt=sinfo[SI_LASTLIGHT])) {
+    ModifySkill(sname,([SI_LASTLIGHT:time()]));
+    dt=0;
+  } else {
+    dt=time()-llt;
+    if (dt<0) dt=0;
+    if (dt>86400) dt=86400;
+  }
+
+  res=(abil*dt)/(20*MAX_ABILITY)+light;
+  if (res<=0) {
+    res--; // Wert muss !=0 sein
+    if (res<-MAX_ABILITY) res=-MAX_ABILITY;
+  } else {
+    if (res>MAX_ABILITY) res=MAX_ABILITY;
+  }
+  return res;
+}
+
+protected mapping StdSkill_Bihand(object me, string sname, mapping sinfo) {
+  int abil,val;
+
+  // printf("Bihand: %O\n",sinfo);
+  if (!sinfo) return 0;
+  abil=sinfo[SI_SKILLABILITY];
+  val=(abil*(QueryAttribute(A_STR)+33))/MAX_ABILITY;
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+  // + max. 53
+  return sinfo;
+}
+
+protected mapping StdSkill_Fight_sword(object me, string sname, mapping sinfo) {
+  int abil,asig,val;
+
+  abil=sinfo[SI_SKILLABILITY];asig=SIG(abil);
+  val=(abil*(QueryAttribute(A_STR) +
+             QueryAttribute(A_DEX)*asig +
+             33))/MAX_ABILITY;
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+  // + max. 73
+  return sinfo;
+}
+
+protected mapping StdSkill_Fight_hands(object me, string sname, mapping sinfo) {
+  int abil,asig,val;
+
+  if (!sinfo) return 0;
+  abil=sinfo[SI_SKILLABILITY];asig=SIG(abil);
+  val=(abil*(QueryAttribute(A_STR) +
+             QueryAttribute(A_DEX)*3*asig +
+             100))/MAX_ABILITY;
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+  // + max. 180
+  return sinfo;
+}
+
+protected int StdSkill_Booze(object me, string sname, mapping sinfo) {
+  int abil,val;
+
+  val=0;
+  if (!sinfo || (val=sinfo[SI_SKILLARG])<=0)
+    return val;
+  abil=sinfo[SI_SKILLABILITY];
+  val-=(val*abil)/(MAX_ABILITY+2500); // Bis zu 80% Abzug bei Alkoholikern.
+  if (val<=0) val=1;
+  return val;
+}
+
diff --git a/std/living/team.c b/std/living/team.c
new file mode 100644
index 0000000..2b31db9
--- /dev/null
+++ b/std/living/team.c
@@ -0,0 +1,650 @@
+// MorgenGrauen MUDlib
+//
+// living/team.c
+//
+// $Id: team.c 9138 2015-02-03 21:46:56Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <properties.h>
+#include <thing/properties.h>
+#include <living/combat.h>
+#include <combat.h>
+#include <living/team.h>
+#include <wizlevels.h>
+#include <hook.h>
+
+#define ME this_object()
+#define TP this_player()
+#define PO previous_object()
+#define ENV environment()
+
+private nosave string team_attack_cmd;
+private nosave mapping team_follow_todo;
+private nosave int team_autofollow;
+private nosave object teammove;
+
+void create() {
+  Set(P_TEAM_ATTACK_CMD,-1,F_SET_METHOD);
+  Set(P_TEAM_ATTACK_CMD,PROTECTED,F_MODE_AS);
+  Set(P_TEAM_AUTOFOLLOW,-1,F_SET_METHOD);
+  Set(P_TEAM_AUTOFOLLOW,PROTECTED,F_MODE_AS);
+  teammove=0;
+  offerHook(H_HOOK_TEAMROWCHANGE, 1);
+}
+
+void add_team_commands() {
+  add_action("teamcmd","gruppe");
+  add_action("teamcmd","g");
+  add_action("teamcmd","team");
+}
+
+string _query_team_attack_cmd() {
+  return Set(P_TEAM_ATTACK_CMD,team_attack_cmd);
+}
+
+int _query_team_autofollow() {
+  return Set(P_TEAM_AUTOFOLLOW,team_autofollow);
+}
+
+private int team_help() {
+  // Syntax-Kompatiblitaet (Avalon) ist ganz nett :-)
+  write("\
+(Befehle des Teamleiters sind mit * gekennzeichnet\n\
+\n\
+* team angriff\n\
+  team angriffsbefehl <befehl>\n\
+* team aufnahme <name>\n\
+  team autof[olge] <ein/aus>\n\
+* team autoi[nfo] <ein/aus> [+[lp]] [+[kp]] [sofort]\n\
+* team entlasse <name>\n\
+  team farben lp_rot lp_gelb kp_rot kp_gelb\n\
+  team flucht[reihe] <reihe>\n\
+  team folge <name>\n\
+* team formation <min[-max]> [<min[-max]> ...]\n\
+  team hilfe|?\n\
+  team [info] [sortiert|alphabetisch]\n\
+  team [kampf]reihe <reihe>\n\
+* team leiter[in] <name>\n\
+  team liste\n\
+* team name <gruppenname>\n\
+  team orte [alle]\n\
+  team ruf[e]\n\
+  team uebersicht\n\
+  team verlasse\n");
+  return 1;
+}
+
+object IsTeamLeader() {
+  object team;
+
+  if (!objectp(team=Query(P_TEAM))
+      || team!=Query(P_TEAM_LEADER)
+      || team->Leader()!=ME)
+    return 0;
+  return team;
+}
+
+object *TeamMembers() {
+  object team;
+
+  if (!objectp(team=Query(P_TEAM)))
+    return ({ME});
+  return team->Members();
+}
+
+string TeamPrefix() {
+  object team;
+
+  if (!objectp(team=Query(P_TEAM)))
+    return "";
+  return "["+team->Name()+"] ";
+}
+
+
+private int team_aufnahmewunsch(string arg) {
+  object pl;
+
+  if ((!objectp(pl=find_player(arg)) && !objectp(pl=present(arg,ENV)))
+      || pl->QueryProp(P_INVIS) || environment(pl)!=ENV)
+    return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
+  if (!living(pl))
+    return notify_fail(pl->Name(WER)+" ist etwas zu inaktiv.\n"),0;
+  if (pl==ME)
+    return notify_fail("Du bist eine Person zu wenig fuer ein Team.\n"),0;
+  SetProp(P_TEAM_NEWMEMBER,pl);
+  if (pl->IsTeamLeader()) {
+    write("Du bittest "+pl->name(WEN)+" um Aufnahme ins Team.\n");
+    tell_object(pl,TP->Name(WER)+" bittet Dich um Aufnahme ins Team.\n");
+  } else {
+    write("Du bittest "+pl->name(WEN)+" um Gruendung eines Teams.\n");
+    tell_object(pl,TP->Name(WER)+" bittet Dich um Gruendung eines Teams.\n");
+  }
+  return 1;
+}
+
+private int team_aufnahme(string arg) {
+  object pl,team;
+  int res;
+
+  if ((!objectp(pl=find_player(arg)) && !objectp(pl=present(arg,ENV)))
+      || pl->QueryProp(P_INVIS) || environment(pl)!=ENV)
+    return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
+  if (pl->QueryProp(P_TEAM_NEWMEMBER)!=ME)
+    return notify_fail(pl->Name(WER)+" hat Dich nicht um Aufnahme gebeten.\n"),
+      0;
+  if (pl==ME)
+    return notify_fail("Du bist eine Person zu wenig fuer ein Team.\n"),0;
+  if (!objectp(team=QueryProp(P_TEAM)))
+    team=clone_object(TEAM_OBJECT);
+  res=team->AddMember(pl);
+  if (!sizeof(team->Members()))
+    team->remove();
+  return res;
+}
+
+object IsTeamMove() {
+  if (!objectp(teammove) || (teammove!=Query(P_TEAM)))
+    teammove=0;
+  return teammove;
+}
+
+static void DoTeamAttack(object env, object callbackto) {
+  if (env==ENV && stringp(team_attack_cmd) && !IS_LEARNER(ME)
+      && (interactive(ME) || !query_once_interactive(ME))
+      && objectp(callbackto) && callbackto==Query(P_TEAM)) {
+    teammove=callbackto;
+    command(team_attack_cmd);
+  }
+  if (objectp(callbackto))
+    callbackto->TeamAttackExecuted_Callback(teammove?1:0);
+  teammove=0;
+}
+
+int CallTeamAttack(object env) {
+  if (stringp(team_attack_cmd)
+      && find_call_out("DoTeamAttack")<0
+      && PO
+      && PO==Query(P_TEAM))
+    return call_out("DoTeamAttack",0,env,PO),1;
+  return 0;
+}
+
+static int DoTeamFollow() {
+  string cmd;
+
+  if (!team_autofollow
+      || (!interactive(ME) && query_once_interactive(ME))
+      || IS_LEARNER(ME)
+      || !mappingp(team_follow_todo))
+    return 0;
+  if (!stringp(cmd=team_follow_todo[ENV]))
+    return team_follow_todo=0;
+
+  do {
+    m_delete(team_follow_todo,ENV);
+    tell_object(ME,sprintf("Du folgst Deinem Team mit \"%s\".\n",cmd));
+    command(cmd);
+  } while (get_eval_cost()>900000 && random(1000)>20 && objectp(ME)
+           && stringp(cmd=team_follow_todo[ENV]));
+
+  // Ist Spieler in Umgebung gelandet, fuer die noch ein
+  // Befehl auszufuehren ist?
+  if (!objectp(ME) || !stringp(team_follow_todo[ENV]))
+    return team_follow_todo=0;
+  while  (remove_call_out("DoTeamFollow")!=-1) ;
+  call_out("DoTeamFollow",0);
+  return 0;
+}
+
+int CallTeamFollow(object env, string cmd) {
+  if (!team_autofollow
+      || PO!=Query(P_TEAM)
+      || !PO
+      || !objectp(env)
+      || !stringp(cmd))
+    return 0;
+  if (!mappingp(team_follow_todo))
+    team_follow_todo=([]);
+  if (ENV!=env && !team_follow_todo[ENV])
+    return 0;
+  team_follow_todo[env]=cmd;
+  if (find_call_out("DoTeamFollow")<0)
+    call_out("DoTeamFollow",0);
+  return 1;
+}
+
+int ClearTeamFollow() {
+  if (PO!=Query(P_TEAM) || !PO)
+    return 0;
+  team_follow_todo=([]);
+  return 1;
+}
+
+mixed *PresentTeamRows() {
+  object team;
+  mixed *res;
+  int i;
+
+  if (!objectp(team=Query(P_TEAM))) {
+    res=EMPTY_TEAMARRAY;
+    res[0]=({ME});
+    return res;
+  }
+  res=team->PresentRows(ENV);
+  for (i=0;i<MAX_TEAMROWS;i++)
+    if (member(res[i],ME)>=0)
+      return res;
+  res[0]+=({ME});
+  return res;
+}
+
+varargs mixed *PresentEnemyRows(object *here) {
+  mixed *res,*rows;
+  mapping added_teams;
+  int i,j;
+  object ob,team;
+
+  added_teams=([Query(P_TEAM):1]); // Nicht auf eigenes Team hauen
+  res=EMPTY_TEAMARRAY;
+  if (!pointerp(here))
+    here=PresentEnemies();
+  for (i=sizeof(here)-1;i>=0;i--) {
+    if (!objectp(ob=here[i]))
+      continue;
+    if (!objectp(team=ob->QueryProp(P_TEAM))) {
+      res[0]+=({ob});
+      continue;
+    }
+    if (added_teams[team])
+      continue;
+    added_teams[team]=1;
+    rows=team->PresentRows(ENV);
+    for (j=0;j<MAX_TEAMROWS;j++)
+      res[j]+=rows[j];
+  }
+  return res;
+}
+
+varargs object SelectNearEnemy(object *here, int forcefrom) {
+  object ob,en,team;
+  mixed *rows;
+  int *prob,prot,i,r,sz,upsz,sum;
+
+  if (!pointerp(here))
+    here=PresentEnemies();
+  if (!objectp(ob=SelectEnemy(here)))
+    return 0;
+  en=ob->QueryProp(P_TEAM);          // Feindliches Team
+  if (objectp(team=Query(P_TEAM))) { // Eigenes Team
+    if (en==team) // Feind im eigenen Team, kein ANDERES Mitglied waehlen.
+      return ob;  // Aber auch ausserhalb Reihe 1 draufhauen
+    rows=team->PresentRows(ENV);
+    if (member(rows[0],ME)<0) // Stehe ich in der ersten Reihe?
+      return 0; // Falls nein ist auch kein Gegner nahe.
+  }
+  if (!objectp(en))
+    return ob; // Ist nicht in einem Team, also drauf.
+  rows=en->PresentRows(environment(ob));
+  prob=({1,0,0,0,0});
+  prot=sum=0;
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    if (prot>0) prot--;                // Schutzkegel nimmt ab.
+    if (!sz=sizeof(rows[i])) continue; // Gegner in dieser Reihe
+    upsz=sz-prot;if (upsz<0) continue; // Anzahl ungeschuetzter Gegner
+    prob[i]+=(upsz+sum);               // Wahrscheinlichkeit += ungeschuetzt
+    sum=prob[i];                       // Summe bisheriger Wahrscheinlichkeiten
+    if (sz>prot) prot=sz;              // Neuer Schutzkegel
+  }
+  r=random(sum);
+  for (i=0;i<MAX_TEAMROWS;i++)
+    if (r<prob[i])
+      break;
+  if (i>=MAX_TEAMROWS)
+    i=0;
+  if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
+    return en;
+  if (i && objectp(en=SelectEnemy(forcefrom?(here&rows[0]):rows[0])))
+    return en;
+  return ob;
+}
+
+varargs object SelectFarEnemy(object *here, int min, int max, int forcefrom) {
+  mixed *rows;
+  int *prob,i,r,sum;
+  object en;
+
+  if (max<0 || min>=MAX_TEAMROWS || max<min)
+    return 0;
+  if (min<0) min=0;
+  if (max>=MAX_TEAMROWS) max=MAX_TEAMROWS-1;
+  if (!pointerp(here))
+    here=PresentEnemies();
+  rows=PresentEnemyRows(here);
+  prob=({0,0,0,0,0});
+  sum=0;
+  for (i=min;i<=max;i++)
+    sum=prob[i]=sum+sizeof(rows[i])+max-i;
+
+  r=random(sum);
+  for (i=min;i<=max;i++)
+    if (r<prob[i])
+      break;
+  if (i>max)
+    i=min;
+  if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
+    return en;
+  for (i=min;i<=max;i++)
+    if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
+      return en;
+  return 0;
+}
+
+mixed _query_friend() {
+  mixed res;
+
+  if (res=Query(P_FRIEND))
+    return res;
+  if (objectp(res=Query(P_TEAM_ASSOC_MEMBERS))
+      && query_once_interactive(res))
+    return res;
+  return 0;
+}
+
+int DeAssocMember(object npc) {
+  mixed obs;
+  object team;
+
+  if (extern_call() && PO!=npc &&
+      member(({"gilden","spellbooks"}),
+             explode(object_name(PO),"/")[1])<0)
+    return 0;
+  obs=QueryProp(P_TEAM_ASSOC_MEMBERS);
+  if (!pointerp(obs))
+    return 0;
+  obs-=({npc,0});
+  SetProp(P_TEAM_ASSOC_MEMBERS,obs);
+  if (objectp(team=QueryProp(P_TEAM)))
+    team->RemoveAssocMember(ME,npc);
+  return 1;
+}
+
+int AssocMember(object npc) {
+  mixed obs;
+  object team;
+
+  if (extern_call() && PO!=npc &&
+      member(({"gilden","spellbooks"}),
+             explode(object_name(PO),"/")[1])<0)
+    return 0;
+  if (!objectp(npc)
+      || npc->QueryProp(P_TEAM_ASSOC_MEMBERS)
+      || IsEnemy(npc)
+      || npc==ME
+      || query_once_interactive(npc))
+    return 0;
+  obs=QueryProp(P_TEAM_ASSOC_MEMBERS);
+  if (objectp(obs))
+    return 0;
+  if (!pointerp(obs))
+    obs=({});
+  obs=(obs-({npc,0}))+({npc});
+  SetProp(P_TEAM_ASSOC_MEMBERS,obs);
+  npc->SetProp(P_TEAM_ASSOC_MEMBERS,ME);
+  if (objectp(team=QueryProp(P_TEAM)))
+    team->AddAssocMember(ME,npc);
+  return 1;
+}
+
+varargs void InsertEnemyTeam(mixed ens, int rek) {
+  object *obs,ob,eteam,team;
+  int i;
+
+  team=Query(P_TEAM);
+  // Alle Teammitglieder des Gegners sind Feind:
+  if (objectp(ens)) {
+    if (objectp(eteam=ens->QueryProp(P_TEAM))) {
+      if (eteam==team) // feindliches Team = eigenes Team?
+        return;        // also nicht alle Teammitglieder gegeneinander hetzen
+      ens=eteam->Members();
+    } else {
+      ens=({ens});
+    }
+  }
+  if (!pointerp(ens))
+    return;
+  ens-=({ME});
+
+  // Interactives sollen keine Interactives durch Team angreifen:
+  if (query_once_interactive(ME)) {
+    for (i=sizeof(ens)-1;i>=0;i--)
+      if (objectp(ob=ens[i]) && environment(ob)==environment()
+          && !query_once_interactive(ob))
+        InsertSingleEnemy(ob);
+  } else {
+    for (i=sizeof(ens)-1;i>=0;i--)
+      if (objectp(ob=ens[i]) && environment(ob)==environment())
+        InsertSingleEnemy(ob);
+  }
+
+  // Alle anderen Teammitglieder Informieren:
+  if (rek || !objectp(team) || !pointerp(obs=team->Members()))
+    return;
+  obs-=({ME});
+  obs-=ens;
+  for (i=sizeof(obs)-1;i>=0;i--)
+    if (objectp(ob=obs[i]))
+      ob->InsertEnemyTeam(ens,1);
+}
+
+int TeamFlee() {
+  object team;
+
+  if (Query(P_TEAM_WIMPY_ROW)<2 || !objectp(team=Query(P_TEAM)))
+    return 0;
+  if (!team->FleeToRow(ME))
+     return 0;
+  if (Query(P_TEAM_LEADER)==team) {
+    if (team_autofollow)
+      tell_object(ME,"Du versuchst zu fliehen, "+
+                  "Dein Team folgt Dir nicht mehr.\n");
+    team_autofollow=0;
+  }
+  return 1;
+}
+
+varargs mapping PresentTeamPositions(mixed pres_rows) {
+  mapping res;
+  int i,j;
+  object *obs,ob;
+
+  res=([]);
+  if (!pointerp(pres_rows))
+    pres_rows=PresentTeamRows();
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    obs=pres_rows[i];
+    for (j=sizeof(obs)-1;j>=0;j--)
+      if (objectp(ob=obs[j]) && !res[ob])
+        res[ob]=i+1;
+  }
+  return res;
+}
+
+varargs int PresentPosition(mixed pmap) {
+  object team;
+  int i;
+
+  if (!objectp(team=Query(P_TEAM)))
+    return 1;
+  if (mappingp(pmap))
+    return pmap[ME];
+  if (!pointerp(pmap))
+    pmap=team->PresentRows(ENV);
+  for (i=1;i<MAX_TEAMROWS;i++)
+    if (member(pmap[i],ME)>=0)
+      return i+1;
+  return 1;
+}
+
+#define FILLSTRING "                                        "
+varargs private string center_string(string str, int w) {
+  return (FILLSTRING[0..((w-sizeof(str))/2-1)]+str+FILLSTRING)[0..(w-1)];
+}
+
+private int ShowTeamRows() {
+  int i,j,sz;
+  mixed *pres_rows;
+  object *obs,ob;
+  string str;
+
+  pres_rows=PresentEnemyRows();
+  for (sz=MAX_TEAMROWS-1;sz>=0;sz--)
+    if (sizeof(pres_rows[sz]))
+      break;
+  for (i=sz;i>=0;i--) {
+    obs=pres_rows[i];str="";
+    for (j=sizeof(obs)-1;j>=0;j--)
+      if (objectp(ob=obs[j])) {
+        if (str!="") str+=" / ";
+        str+=ob->Name(RAW);
+      }
+    printf("%d. %s\n",i+1,center_string(str,75));
+  }
+  if (sz>=0)
+    write("   ---------------------------------------------------------------------------\n");
+  pres_rows=PresentTeamRows();
+  for (sz=MAX_TEAMROWS-1;sz>0;sz--)
+    if (sizeof(pres_rows[sz]))
+      break;
+  for (i=0;i<=sz;i++) {
+    obs=pres_rows[i];str="";
+    for (j=sizeof(obs)-1;j>=0;j--)
+      if (objectp(ob=obs[j])) {
+        if (str!="") str+=" / ";
+        str+=ob->Name(RAW);
+      }
+    printf("%d. %s\n",i+1,center_string(str,75));
+  }
+  return 1;
+}
+
+varargs int team_list(string arg) {
+  object *tobs,*obs,tob,ob,ld;
+  string *nms,*tnms,str;
+  int i,j;
+
+  if (!pointerp(tobs=TEAM_MASTER->ListTeamObjects())) return 0;
+  if (arg!="alle") arg=0;
+  tnms=({});
+  for (i=sizeof(tobs)-1;i>=0;i--) {
+    if (!objectp(tob=tobs[i])
+        || !objectp(ld=tob->Leader())
+        || (!query_once_interactive(ld) && !arg)
+        || !pointerp(obs=tob->Members()))
+      continue;
+    nms=({});
+    for (j=sizeof(obs)-1;j>=0;j--) {
+      if (!objectp(ob=obs[j])
+          || (!query_once_interactive(ob) &&!arg))
+        continue;
+      if (!stringp(str=ob->Name(WER))) str="?";
+      if (ob==ld) str+="(*)";
+      nms+=({str});
+      nms=sort_array(nms,#'>);
+    }
+    if (!stringp(str=tob->Name())) str="Team ?";
+    str+=": ";
+    tnms+=({break_string(implode(nms,", "),78,str)});
+    tnms=sort_array(tnms,#'<);
+  }
+  if (sizeof(tnms))
+    tell_object(ME, sprintf("%@s\n", tnms));
+  else
+    tell_object(ME, "Keine Teams gefunden.\n"); 
+  
+  return 1;
+}
+
+varargs int teamcmd(string arg) {
+  string *words,narg;
+  object team;
+
+  if (!arg)
+    arg="";
+  if (!stringp(narg=TP->_unparsed_args()))
+    narg = arg;
+  if (!sizeof(words=explode(arg," ")))
+    return 0;
+ 
+  if (sizeof(words) > 1) {
+      arg=implode(words[1..]," ");
+      narg = implode(explode(narg, " ")[1..], " ");
+  }
+  else
+      arg = narg = "";
+
+  switch(words[0]) { // Befehle die keine Mitgliedschaft erfordern:
+  case "aufnahme":
+    return team_aufnahme(arg);
+  case "folge":
+    return team_aufnahmewunsch(arg);
+  case "?":
+  case "hilfe":
+    return team_help();
+  case "liste":
+    return team_list(arg);  
+  case "uebersicht":
+    return ShowTeamRows();
+  default:;
+  }
+
+  if (!objectp(team=QueryProp(P_TEAM)))
+    return notify_fail("Du bist in keinem Team.\n"),0;
+
+  switch(words[0]) {
+  case "angriffsbefehl":
+    if (narg=="") narg=0;
+    team_attack_cmd=narg;
+    if (stringp(narg))
+      write("Du beginnst den Kampf mit \""+narg+"\"\n");
+    else
+      write("Du hast den Teamangriffsbefehl deaktiviert.\n");
+    break; // NICHT return!
+  case "autofolge":
+  case "autof":
+    if (arg=="ein" || arg=="an") {
+      team_autofollow=1;
+      if (IsTeamLeader())
+        write("Dein Team folgt Dir.\n");
+      else
+        write("Du folgst jetzt dem Teamleiter.\n");
+    } else {
+      team_autofollow=0;
+      if (IsTeamLeader())
+        write("Dein Team folgt Dir nicht mehr.\n");
+      else
+        write("Du folgst jetzt nicht mehr dem Teamleiter.\n");
+    }
+    break; // NICHT return!
+  default: ;
+  }
+  return team->TeamCmd(words[0],narg); // Befehle die Mitgliedschaft erfordern:
+}
+
+varargs void InformRowChange(int from, int to, object caster) {
+
+  if (caster) return; // Fuer den Fall, dass Gildenobjekt==ME ist
+  if (PO!=Query(P_TEAM)) return;
+#if __BOOT_TIME__ < 1281904437
+  mixed gilde = QueryProp(P_GUILD);
+  if (!stringp(gilde)) return;
+  if (!objectp(gilde=find_object("/gilden/"+gilde))) return;
+  gilde->InformRowChange(from,to,ME);
+#endif
+  HookFlow(H_HOOK_TEAMROWCHANGE, ({from,to}) );
+}