Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/std/npc/combat.c b/std/npc/combat.c
new file mode 100644
index 0000000..747714b
--- /dev/null
+++ b/std/npc/combat.c
@@ -0,0 +1,407 @@
+// MorgenGrauen MUDlib
+//
+// npc/combat.c -- NPC-spezifische Kampffunktionen
+//
+// $Id: combat.c 9488 2016-02-19 21:24:04Z Arathorn $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "std/living/combat";
+
+#include <combat.h>
+#include <language.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+
+#define NEED_PROTOTYPES 1
+#include <living/life.h>
+#undef NEED_PROTOTYPES
+
+#define HB_CHECK 7
+#define ME this_object()
+#define STATMASTER "/p/service/rochus/guildstat/master"
+
+nosave int heartbeat, beatcount;
+
+private void catch_up_hbs();
+
+protected void create() {
+  ::create();
+  beatcount=1;
+  heartbeat=1;
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// aktuelles Lebewesen, fuer das dieser NPC gerade taetig ist. Default:
+// Spieler, bei dem er als Helfer-NPC registriert ist.
+public object QueryUser()
+{
+  mixed helperdata = QueryProp(P_HELPER_NPC);
+  if (pointerp(helperdata) && objectp(helperdata[0]))
+    return helperdata[0];
+  return 0;
+}
+
+// ggf. Feinde expiren. Soll das Problem verringern, dass Spieler nach Tagen
+// erst die Meldung kriegen, dass der NPC sie nicht mehr jagt, wenn der HB
+// reaktivert wird.
+void reset() {
+  // ggf. die abgeschalteten HBs nachholen.
+  if (!heartbeat)
+    catch_up_hbs();
+  // ggf. P_ENEMY_DAMAGE zuruecksetzen
+  ResetEnemyDamage();
+}
+
+static void _set_max_hp(int i) {
+  Set(P_MAX_HP,i);
+  SetProp(P_HP,i);
+}
+
+static void _set_max_sp(int i) {
+  Set(P_MAX_SP,i);
+  SetProp(P_SP,i);
+}
+
+
+// Check-Funktion fuer die P_NO_ATTACK-QueryMethod
+static mixed _check_immortality()
+{
+    int t;
+
+    if ( !(t = Query("time_to_mortality")) || time() > t ){
+        // Zeit ist abgelaufen - wieder angreifbar machen
+        Set( P_NO_ATTACK, 0, F_QUERY_METHOD );
+        heartbeat = 1;
+        beatcount = 1;
+        set_heart_beat(1);
+
+        return 0;
+    }
+
+    // der NPC ist noch unangreifbar
+    return break_string( capitalize(name( WER, 1 )) + " versteckt sich hinter "
+                         "einem Fehler im Raum-Zeit-Gefuege und entgeht so "
+                         "voruebergehend allen Angriffen.", 78 );
+}
+
+
+// wenn der HeartBeat buggt, wird diese Funktion vom Master aufgerufen
+public void make_immortal()
+{
+    // fuer 5 Minuten unangreifbar machen
+    Set( P_NO_ATTACK, #'_check_immortality, F_QUERY_METHOD );
+
+    Set( "time_to_mortality", time() + 300, F_VALUE );
+
+    // damit die Spieler keinen Vorteil durch den Bug haben, heilen
+    heal_self(10000);
+
+    // da nun der Heartbeat abgeschaltet ist und normalerweise erst
+    // reaktiviert wird, sobald jemand nach 5min P_NO_ATTACK abfragt, muss man
+    // aber auf Viecher achten, die immer nen Heartbeat haben wollen. In dem
+    // fall per call_out selber die Prop abfragen.
+    if (QueryProp(P_HB))
+      call_out(#'QueryProp, 301, P_NO_ATTACK);
+}
+
+
+// Damit NPCs gegeneinander weiterkaempfen, auch wenn kein Spieler
+// in der Naehe ist:
+static int _query_hb()
+{
+    // TODO: return InFight() || Query(P_HB, F_VALUE), sobald InFight()
+    // geaendert.
+    return (InFight() || Query(P_HB,F_VALUE)) ? 1 : 0;
+}
+
+
+#define SPELL_TOTALRATE 0
+#define SPELL_DAMAGE 1
+#define SPELL_TEXT_FOR_ENEMY 2
+#define SPELL_TEXT_FOR_OTHERS 3
+#define SPELL_DAMTYPE 4
+#define SPELL_FUNC 5
+#define SPELL_ARG 6
+
+varargs int AddSpell(int rate, int damage, string TextForEnemy,
+                     string TextForOthers, string|string* dam_type, string func,
+                     int|mapping spellarg) {
+  mixed *spells;
+  int total_rates;
+
+  if (rate<0 || damage<=0 || !stringp(TextForEnemy) ||
+      !stringp(TextForOthers))
+     return 0;
+
+  if (stringp(dam_type))
+     dam_type = ({dam_type});
+  else if (!pointerp(dam_type))
+      dam_type = ({DT_MAGIC});
+  
+  // Tatsaechlich ist es immer ein nicht-physischer Angriff, wenn spellarg ein
+  // int ist, weil wenn spellarg==0 ist der Default nicht-physischer Angriff
+  // und bei spellarg!=0 auch. Nur mit einem mapping kann man einen phys.
+  // Angriff erzeugen.
+  if (intp(spellarg))
+    spellarg = ([SP_PHYSICAL_ATTACK: 0]);
+
+  // Falls vorhanden, alte Syntax auf die von replace_personal() anpassen,
+  // die im heart_beat() beim Ausgeben der Meldung verwendet wird.
+  if ( strstr(TextForOthers, "@", 0) != -1 )
+  {
+    // Zeichen nach @WER & Co in runde Klammern einschliessen, damit es als
+    // Sub-Pattern im Ausgabestring wiederholt werden kann. Ansonsten wuerde
+    // es mit ersetzt.
+    TextForOthers = regreplace(TextForOthers, "@WER([^1-9])", "@WER1\\1", 1);
+    TextForOthers = regreplace(TextForOthers, "@WESSEN([^1-9])",
+                               "@WESSEN1\\1", 1);
+    TextForOthers = regreplace(TextForOthers, "@WEM([^1-9])", "@WEM1\\1", 1);
+    TextForOthers = regreplace(TextForOthers, "@WEN([^1-9])", "@WEN1\\1", 1);
+  }
+  total_rates=Query("npc:total_rates")+rate;
+  spells=Query(P_SPELLS);
+  if (!pointerp(spells))
+    spells=({});
+  spells+=({({total_rates, damage, TextForEnemy, TextForOthers,
+              dam_type, func, spellarg})});
+  Set(P_SPELLS,spells);
+  Set("npc:total_rates",total_rates);
+  return 1;
+}
+
+int AutoAttack(object ob) {
+  mixed m;
+
+  if (!query_once_interactive(ob))
+    return 0;
+  if (mappingp(m=QueryProp(P_AGGRESSIVE))) {
+    mixed *ind,x,z;
+    float f;
+    int i,n;
+
+    ind=m_indices(m)-({0});n=0;f=0.0;
+    for (i=sizeof(ind)-1;i>=0;i--) {
+      x=ind[i];
+      if ((z=m[x][ob->QueryProp(x)]) || (z=m[x][0])) {
+  f=f+(float)z;
+  n++;
+      }
+    }
+    if (n)
+      m=f/((float)n);
+    else
+      m=m[0];
+  }
+  if (((int)(100*(m+ob->QueryProp(P_AGGRESSIVE))))<=random(100))
+    return 0;
+  if  (IS_LEARNER(ob)
+       && (ob->QueryProp(P_INVIS)
+     || ob->QueryProp(P_WANTS_TO_LEARN)))
+    return 0;
+  return 1;
+}
+
+void SpellAttack(object enemy) {
+}
+
+#if 0
+TJ(string s) {
+  object o;
+  if (o=find_player("jof"))
+    tell_object(o,sprintf("%O: %s\n",this_object(),s));
+}
+#else
+#define TJ(x)
+#endif
+
+protected void heart_beat() {
+  int r,i;
+  mixed env,*spells, sinfo;
+  object enemy;
+
+  if ( --beatcount < 0 )
+      beatcount = 0;
+  
+  if (!beatcount && !Query(P_HB)) {
+    if (!environment()) {
+      set_heart_beat(0);
+      heartbeat = 0;
+      if( clonep(this_object()) ) remove();
+      return;
+    }
+    if (!QueryProp(P_POISON)) {
+      // Spieler anwesend?
+      env = filter(all_inventory(environment()), #'query_once_interactive);
+      if (!sizeof(env)) {
+	// Nein, HBs abschalten.
+	set_heart_beat(0);
+	heartbeat=0;
+	TJ("OFF\n");
+	beatcount=HB_CHECK;
+	Set("npc:beat_off_num",absolute_hb_count());
+	return;
+      }
+    }
+  }
+  ::heart_beat();
+  if (!ME)
+      return;
+  enemy=SelectEnemy();
+  if (QueryProp(P_AGGRESSIVE)
+      && (!enemy || environment()!=environment(enemy))
+      && !beatcount) {
+    beatcount=HB_CHECK;
+    env=filter(all_inventory(environment()),#'AutoAttack);
+    if (!sizeof(env))
+       return;
+    i=random(sizeof(env));
+    Kill(env[i]);
+  }
+  else if (!beatcount)
+    beatcount=HB_CHECK;
+  if (!objectp(enemy) ||QueryProp(P_DISABLE_ATTACK)>0)
+    return;
+  SpellAttack(enemy);
+
+  if (!pointerp(spells=Query(P_SPELLS))
+      || !sizeof(spells)
+      || !objectp(enemy=SelectEnemy())
+      || environment(enemy)!=environment()
+      || (QueryProp(P_DISABLE_ATTACK)>0)
+      || random(100)>Query(P_SPELLRATE))
+    return;
+  r=random(Query("npc:total_rates"));
+  for (i=sizeof(spells)-1;(i>0 && spells[i-1][SPELL_TOTALRATE]>r);i--)
+    ;
+  string akt_spell_mess = spells[i][SPELL_TEXT_FOR_ENEMY];
+  if ( sizeof(akt_spell_mess) )
+    tell_object(enemy, break_string(akt_spell_mess, 78));
+  akt_spell_mess = spells[i][SPELL_TEXT_FOR_OTHERS];
+  // Nur, wenn ueberhaupt eine Meldung gesetzt wurde, muss diese verarbeitet
+  // werden.
+  if (stringp(akt_spell_mess) && sizeof(akt_spell_mess))
+  {
+    akt_spell_mess = replace_personal(akt_spell_mess, ({enemy}), 1);
+    say(break_string(akt_spell_mess, 78),({enemy, this_object()}));
+  }
+  sinfo = deep_copy(spells[i][SPELL_ARG]);
+  if(!mappingp(sinfo))
+    sinfo=([ SI_MAGIC_TYPE :({ MT_ANGRIFF }) ]);
+  else if(!sinfo[SI_MAGIC_TYPE])
+    sinfo[ SI_MAGIC_TYPE]=({ MT_ANGRIFF });
+  if(!sinfo[SP_PHYSICAL_ATTACK] &&
+     (enemy->SpellDefend(this_object(),sinfo) >
+      random(MAX_ABILITY+QueryProp(P_LEVEL)*50))){
+    tell_object(enemy,"Du wehrst den Spruch ab.\n");
+    say(enemy->Name(WER,1)+" wehrt den Spruch ab.\n",
+        ({ enemy, this_object()}));
+    return ;
+  }
+  int damage = random(spells[i][SPELL_DAMAGE])+1;
+  enemy->Defend(damage, spells[i][SPELL_DAMTYPE],
+                spells[i][SPELL_ARG],
+                this_object());
+
+  // Falls der Gegner (oder wir) im Defend stirbt, hier abbrechen
+  if ( !objectp(ME) || !objectp(enemy) 
+      || enemy->QueryProp(P_GHOST) ) return;
+  
+  if (spells[i][SPELL_FUNC] && stringp(spells[i][SPELL_FUNC]))
+    catch(call_other(this_object(),
+         spells[i][SPELL_FUNC],
+         enemy,
+         damage,
+         spells[i][SPELL_DAMTYPE]);publish);
+}
+
+// Heartbeats nachholen.
+private void catch_up_hbs() {
+  // gibt es HBs zum nachholen?
+  int beat_off_num = Query("npc:beat_off_num");
+  if (!beat_off_num)
+    return; // nein.
+  // wieviele HBs nachholen?
+  beat_off_num = absolute_hb_count() - beat_off_num;
+  
+  if (beat_off_num>0) {
+    // Nicht ausgefuehrtes HEILEN nachholen
+    int rlock=QueryProp(P_NO_REGENERATION);
+    int hp=QueryProp(P_HP);
+    int sp=QueryProp(P_SP);
+    int alc=QueryProp(P_ALCOHOL);
+    if (!(rlock & NO_REG_HP)) {
+      hp+=beat_off_num/HEAL_DELAY+alc/ALCOHOL_DELAY;
+      SetProp(P_HP,hp);
+    }
+    if (!(rlock & NO_REG_SP)) {
+      sp+=beat_off_num/HEAL_DELAY+alc/ALCOHOL_DELAY;
+      SetProp(P_SP,sp);
+    }
+    alc-=beat_off_num/ALCOHOL_DELAY;
+    if ( alc < 0 )
+      alc = 0;
+    SetProp(P_ALCOHOL,alc);
+    int da = QueryProp(P_DISABLE_ATTACK);
+    // Paralysen abbauen
+    if ( da > 0 ) {
+      da -= beat_off_num;
+      if ( da < 0 )
+	da = 0;
+      SetProp( P_DISABLE_ATTACK, da );
+    }
+    // Hunttimes aktualisieren, Feinde expiren
+    update_hunt_times(beat_off_num);
+    if (!heartbeat)
+      // HBs immer noch abgeschaltet, naechstes Mal HBs seit jetzt nachholen.
+      Set("npc:beat_off_num",absolute_hb_count());
+    else
+      // HB laeuft wieder, nix mehr nachholen, bis zur naechsten Abschaltung.
+      Set("npc:beat_off_num",0);
+  }
+}
+
+void init() {
+
+  // ggf. Heartbeats nachholen und wieder einschalten.
+  if (!heartbeat) {
+    set_heart_beat(1);
+    heartbeat=1;
+    catch_up_hbs();
+  }
+
+  if (AutoAttack(this_player()))
+    Kill(this_player());
+}
+
+private nosave closure mod_att_stat;
+
+int Defend(int dam, mixed dam_type, mixed spell, object enemy) {
+  if (objectp(enemy=(enemy||this_player()))
+      && query_once_interactive(enemy)
+      && !IS_LEARNER(enemy)) {
+    if (!objectp(get_type_info(mod_att_stat,2))) {
+      object ma;
+      if (!objectp(ma=find_object(STATMASTER)))
+        return ::Defend(dam,dam_type,spell,enemy);
+      // Keine Statistik wenn Master nicht geladen ist.
+      mod_att_stat=symbol_function("ModifyAttackStat",ma);
+    }
+    funcall(mod_att_stat,
+            enemy->QueryProp(P_GUILD),
+            enemy->QueryProp(P_GUILD_LEVEL),
+            dam,
+            dam_type,
+            spell);
+  }
+
+  return ::Defend(dam,dam_type,spell,enemy);
+}