Added public files

Roughly added all public files. Probably missed some, though.
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;
+}
+