Added public files

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