Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/player/life.c b/std/player/life.c
new file mode 100644
index 0000000..73f1ff7
--- /dev/null
+++ b/std/player/life.c
@@ -0,0 +1,841 @@
+// MorgenGrauen MUDlib
+//
+// player/life.c -- player life handling
+//
+// $Id: life.c 9397 2015-12-11 20:29:26Z Zesstra $
+
+// Defines some things which are different than in living.c
+// One example is the heart_beat().
+
+// Properties
+// P_AGE -- Age
+
+#pragma strong_types, save_types, rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/life";
+
+#include <rtlimits.h>
+#include <debug_info.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <player/base.h>
+#include <player/life.h>
+#include <moving.h>
+#include <player/comm.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <language.h>
+#include <properties.h>
+#include <hook.h>
+
+#include <living/life.h>
+#include <player/pklog.h>
+#include <player/combat.h>
+#include <health.h>
+#include <living/combat.h>
+#include <attributes.h>
+#include <defuel.h>
+#include <new_skills.h>
+
+// Die Folgen eines Todes wirken 4 Stunden lang nach
+#define SUFFER_TIME 7200
+#define P_DEATH_INFO "death_info"
+
+int age; /* Number of heart beats of this character. */
+
+private int suffer_time;
+static int time_to_save; /* when to next save player */
+private nosave int die_in_progress; // semaphore fuer die()-Aufrufe.
+
+static int _set_playerkills( int val );
+
+protected void create()
+{
+ ::create();
+ Set(P_AGE, -1, F_SET_METHOD);
+ Set(P_AGE, PROTECTED, F_MODE);
+ Set( P_KILLS, SAVE|SECURED, F_MODE_AS );
+ Set( P_GHOST, SAVE, F_MODE_AS );
+ Set( P_TIMING_MAP, SAVE|SECURED, F_MODE_AS );
+ Set( P_LAST_DEATH_TIME, SAVE|SECURED, F_MODE_AS );
+ Set( P_DEATH_INFO, SAVE|SECURED, F_MODE_AS );
+ Set( P_DEFUEL_LIMIT_FOOD,PROTECTED,F_MODE_AS);
+ Set( P_DEFUEL_LIMIT_DRINK,PROTECTED,F_MODE_AS);
+ Set( P_DEFUEL_TIME_FOOD,PROTECTED,F_MODE_AS);
+ Set( P_DEFUEL_TIME_DRINK,PROTECTED,F_MODE_AS);
+ Set( P_DEFUEL_AMOUNT_FOOD,PROTECTED,F_MODE_AS);
+ Set( P_DEFUEL_AMOUNT_DRINK,PROTECTED,F_MODE_AS);
+ offerHook(H_HOOK_HP,1);
+ offerHook(H_HOOK_SP,1);
+ // P_TIMING_MAP aufraeumen, aber zeitverzoegert, weil jetzt die Daten noch
+ // nicht aus dem Savefile geladen wurden.
+ call_out(#'expire_timing_map, 4);
+}
+
+// called from base.c in Reconnect()
+protected void reconnect() {
+ expire_timing_map();
+}
+
+static int _set_playerkills( int val )
+{
+ string tmp;
+ int playerkills;
+
+ // ist der Setzer in einer Arena/Schattenwelt. Dann nicht. (Ja, Bug ist,
+ // dass EMs aus Schattenwelt/Arena heraus auch das PK-Flag nicht
+ // zuruecksetzen koennen.)
+ if ( previous_object(1) && environment(previous_object(1)) &&
+ (tmp = object_name(environment(previous_object(1)))) &&
+ CheckArenaFight(previous_object(1)) )
+ return 0;
+
+ tmp = sprintf( "%O: %s %O %s",
+ previous_object(1) || this_interactive() || this_player(),
+ getuid(ME), val, dtime(time()) );
+
+ playerkills = Query(P_KILLS);
+
+ if( intp(val) && val >= 0 )
+ if( previous_object(1) && IS_ARCH(getuid(previous_object(1))) &&
+ this_interactive() && IS_ARCH(this_interactive()) )
+ playerkills = val;
+ else
+ tmp += " ILLEGAL!";
+ else
+ playerkills++;
+
+ if ( !previous_object(1) || !query_once_interactive(previous_object(1)) ||
+ IS_LEARNER(previous_object(1)) )
+ log_file( "SET_KILLS", tmp + "\n" );
+
+ return Set( P_KILLS, playerkills );
+}
+
+
+public int death_suffering()
+{
+ if ( suffer_time <= 0 )
+ return suffer_time = 0;
+
+ return 1 + (10 * suffer_time) / SUFFER_TIME;
+}
+
+
+protected void heart_beat()
+{
+ mapping di, mods;
+
+ ++age; // erstmal altern ;-)
+
+ ::heart_beat();
+
+ if ( age > time_to_save ){
+ save_me(1);
+ time_to_save = age + 500;
+ }
+
+ // als geist hat man mit den meisten weltlichen Dingen nicht allzuviel zu
+ // tun.
+ if ( QueryProp(P_GHOST) )
+ return;
+
+ if ( suffer_time > 0 )
+ suffer_time--;
+
+ // Todesfolgen nur alle 20 Sekunden (10 HB) checken.
+ // Das ist immer noch oft genug und spart Rechenzeit.
+ if ( (age % 10) || !mappingp(di = QueryProp(P_DEATH_INFO)) )
+ return;
+
+ mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"];
+ if (!mappingp(mods)) return;
+
+ if ( mods[A_STR] && --di[A_STR] <= 0) {
+ // einen Attributspunkt regenerieren
+ if ( mods[A_STR] < -1 ) {
+ mods[A_STR]++;
+ di[A_STR] = (110 + 5 * (di[A_STR, 1] + mods[A_STR])) / 10;
+ }
+ else {
+ m_delete( mods, A_STR );
+ m_delete( di, A_STR );
+ }
+ }
+
+ if ( mods[A_CON] && --di[A_CON] <= 0) {
+ // einen Attributspunkt regenerieren
+ if ( mods[A_CON] < -1 ){
+ mods[A_CON]++;
+ di[A_CON] = (110 + 5 * (di[A_CON, 1] + mods[A_CON])) / 10;
+ }
+ else {
+ m_delete( mods, A_CON );
+ m_delete( di, A_CON );
+ }
+ }
+
+ if ( mods[A_DEX] && --di[A_DEX] <= 0) {
+ // einen Attributspunkt regenerieren
+ if ( mods[A_DEX] < -1 ){
+ mods[A_DEX]++;
+ di[A_DEX] = (110 + 5 * (di[A_DEX, 1] + mods[A_DEX])) / 10;
+ }
+ else {
+ m_delete( mods, A_DEX );
+ m_delete( di, A_DEX );
+ }
+ }
+
+ if ( mods[A_INT] && --di[A_INT] <= 0) {
+ // einen Attributspunkt regenerieren
+ if ( mods[A_INT] < -1 ){
+ mods[A_INT]++;
+ di[A_INT] = (110 + 5 * (di[A_INT, 1] + mods[A_INT])) / 10;
+ }
+ else {
+ m_delete( mods, A_INT );
+ m_delete( di, A_INT );
+ }
+ }
+
+ if ( sizeof(di) && sizeof(mods))
+ SetProp( P_DEATH_INFO, di );
+ else
+ SetProp( P_DEATH_INFO, 0 );
+
+ SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+}
+
+
+public void force_save() {
+ time_to_save = 0;
+}
+
+
+nomask public int do_damage( int dam, object enemy )
+{
+ int hit_point;
+
+ if( QueryProp(P_GHOST) || dam <= 0 )
+ return 0;
+
+ hit_point = QueryProp(P_HP);
+
+ if ( query_once_interactive(ME) && dam >= hit_point && IS_LEARNING(ME) ){
+ tell_object( ME, "Deine magischen Kraefte verhindern Deinen Tod.\n" );
+ return 0;
+ }
+
+ if ( !objectp(enemy) )
+ enemy = previous_object() || this_interactive() || this_player();
+
+ hit_point -= dam;
+
+ if( hit_point < 0 ){
+ if ( !interactive(ME) )
+ // Netztote sterben nicht
+ hit_point = 10;
+ else {
+ if ( objectp(enemy) && interactive( enemy ) && enemy != ME &&
+ !QueryProp(P_TESTPLAYER) && !IS_WIZARD(ME) &&
+ !CheckArenaFight(ME) ) {
+ if ( QueryPlAttacked(enemy) )
+ hit_point = 1;
+ else {
+ hit_point = 0;
+ enemy->SetProp( P_KILLS, -1 );
+ }
+
+ log_file( "KILLER",
+ sprintf( "%s %s(%d/%d) toetete %s(%d/%d)%s\n",
+ ctime(time()),
+ getuid(enemy), query_wiz_level(enemy),
+ (int) enemy->QueryProp(P_LEVEL), getuid(ME),
+ query_wiz_level(ME), QueryProp(P_LEVEL),
+ (hit_point ? " NOTWEHR=>KEIN PK" : "") ) );
+ }
+ else {
+ string killername;
+ if (objectp(enemy))
+ killername=sprintf("%s (%s)",
+ BLUE_NAME(enemy), REAL_UID(enemy));
+ else
+ killername="??? (???)";
+
+ if ( !QueryProp(P_TESTPLAYER) )
+ create_kill_log_entry(killername, enemy );
+ }
+
+ if ( enemy )
+ enemy->StopHuntFor( ME, 1 );
+
+ map_objects( QueryEnemies()[0], "StopHuntFor", ME, 1 );
+ StopHuntingMode(1);
+
+ Set( P_KILLER, enemy );
+ die();
+ }
+ }
+
+ SetProp( P_HP, hit_point );
+ return dam;
+}
+
+
+// Loescht im sterbenden Spieler die 'koerperabhaengigen' Properties
+private void reset_my_properties()
+{
+ // Loeschen der Properties
+ if ( QueryProp(P_POISON) )
+ {
+ // Don't die twice 'cause of the same poison
+ Set( P_POISON, 0, F_SET_METHOD );
+ Set( P_POISON, 0, F_QUERY_METHOD );
+ SetProp( P_POISON, 0 );
+ }
+
+ Set( P_FROG, 0, F_QUERY_METHOD );
+ Set( P_FROG, 0, F_SET_METHOD );
+ SetProp( P_FROG, 0 ); // Damit die Attribute auch stimmen.
+ Set( P_ALCOHOL, 0, F_QUERY_METHOD );
+ Set( P_ALCOHOL, 0, F_SET_METHOD );
+ SetProp(P_ALCOHOL, 0 );
+ Set( P_DRINK, 0, F_QUERY_METHOD );
+ Set( P_DRINK, 0, F_SET_METHOD );
+ SetProp(P_DRINK, 0 );
+ Set( P_FOOD, 0, F_QUERY_METHOD );
+ Set( P_FOOD, 0, F_SET_METHOD );
+ SetProp(P_FOOD, 0 );
+ Set( P_BLIND, 0, F_QUERY_METHOD );
+ Set( P_BLIND, 0, F_SET_METHOD );
+ SetProp(P_BLIND, 0 );
+ Set( P_DEAF, 0, F_QUERY_METHOD );
+ Set( P_DEAF, 0, F_SET_METHOD );
+ SetProp(P_DEAF, 0 );
+ Set( P_MAX_HANDS, 0, F_QUERY_METHOD );
+ Set( P_MAX_HANDS, 0, F_SET_METHOD );
+ SetProp( P_MAX_HANDS, 2 );
+ Set( P_HANDS_USED_BY, 0, F_QUERY_METHOD );
+ Set( P_HANDS_USED_BY, 0, F_SET_METHOD );
+ SetProp( P_HANDS_USED_BY, ({}) );
+ Set( P_PARA, 0 );
+ Set( P_NO_REGENERATION, 0, F_QUERY_METHOD );
+ Set( P_NO_REGENERATION, 0, F_SET_METHOD );
+ SetProp(P_NO_REGENERATION, 0 );
+ Set( P_TMP_MOVE_HOOK, 0, F_QUERY_METHOD );
+ Set( P_TMP_MOVE_HOOK, 0, F_SET_METHOD );
+ SetProp(P_TMP_MOVE_HOOK, 0 );
+ Set( P_LAST_DEATH_TIME , time() );
+ // damit der Teddy o.ae. mitbekommt, dass man jetzt tot ist ]:->
+ SetProp( P_HP, 0 );
+ SetProp( P_SP, 0 );
+}
+
+varargs protected int second_life( object corpse )
+{
+ int lost_exp, level;
+ // Es gibt Funktionen, die sollte man nicht per Hand aufrufen duerfen ;-)
+ if ( extern_call() && previous_object() != ME )
+ return 0;
+
+ if ( query_once_interactive(ME) && IS_LEARNING(ME) ){
+ tell_object( ME, "Sei froh, dass Du unsterblich bist, sonst waere "
+ "es eben zu Ende gewesen.\n" );
+ return 1;
+ }
+
+ if ( !IS_SEER(ME) || (level = QueryProp(P_LEVEL)) < 20 )
+ lost_exp = QueryProp(P_XP) / 3;
+ else
+ lost_exp = QueryProp(P_XP) / (level - 17);
+
+ AddExp(-lost_exp);
+
+
+ // Todesfolgen setzen....
+ //SetProp( P_DEATH_INFO, 1);
+ if ( !IS_LEARNING(ME) && !QueryProp(P_TESTPLAYER) ) {
+
+ mapping attr = QueryProp(P_ATTRIBUTES);
+ mapping mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
+
+ // Attribute auf 75% vom aktuellen Wert senken
+ mods[A_STR] = -attr[A_STR] + (3 * (attr[A_STR] + mods[A_STR]) / 4);
+ mods[A_CON] = -attr[A_CON] + (3 * (attr[A_CON] + mods[A_CON]) / 4);
+ mods[A_DEX] = -attr[A_DEX] + (3 * (attr[A_DEX] + mods[A_DEX]) / 4);
+ mods[A_INT] = -attr[A_INT] + (3 * (attr[A_INT] + mods[A_INT]) / 4);
+
+ SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+
+ int offs = 220; // 220 heart_beats == 7min20
+ // Die 220 HB sind fix, dazu kommen noch 5 HB pro realem
+ // Attributspunkt. Geteilt wird das ganze noch durch 10, weil im HB
+ // nur alle 10 HBs die TF gecheckt werden. Da wird dann alle 10 HB ein
+ // Punkt abgezogen und wenn 0 erreicht ist, wird das Attribut um eins
+ // regeneriert.
+ SetProp( P_DEATH_INFO, ([
+ A_STR: (offs + 5 * (attr[A_STR] + mods[A_STR]))/10; attr[A_STR],
+ A_CON: (offs + 5 * (attr[A_CON] + mods[A_CON]))/10; attr[A_CON],
+ A_DEX: (offs + 5 * (attr[A_DEX] + mods[A_DEX]))/10; attr[A_DEX],
+ A_INT: (offs + 5 * (attr[A_INT] + mods[A_INT]))/10; attr[A_INT]
+ ]) );
+
+ // die suffer_time wird via death_suffering() von
+ // QuerySkillAttribute() abgefragt und geht dann als Malus in
+ // SA_QUALITY mit ein.
+ if ( suffer_time <= 2*SUFFER_TIME )
+ suffer_time += SUFFER_TIME - 1;
+ else
+ suffer_time = 3 * SUFFER_TIME - 1;
+ }
+
+ // Die verschiedenen NotifyPlayerDeath-Funktionen koennen u.U. schlecht
+ // programmiert sein und zuviel Rechenzeit ziehen. Deshalb werden sie mit
+ // einem Limits von 150k bzw. 80k aufgerufen. Ausserdem werden sie nur
+ // gerufen, solange noch min. 25k Ticks da sind.
+ int *limits=query_limits();
+ limits[LIMIT_EVAL] = 150000;
+ limits[LIMIT_COST] = LIMIT_DEFAULT;
+
+ mixed killer = QueryProp(P_KILLER);
+ mixed gi = QueryProp(P_GUILD);
+ if (stringp(gi))
+ gi = find_object("/gilden/"+gi);
+ // jedes Objekt nur einmal, aber nicht via m_indices(mkmapping)) wegen
+ // Verlust der Reihenfolge.
+ object *items = ({killer});
+ if (environment() != killer)
+ items += ({environment()});
+ if (gi != killer && gi != environment())
+ items += ({gi});
+ foreach(object item: items) {
+ if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
+ break;
+ // falls ein NPD() implizit andere Objekt zerstoert hat.
+ if (objectp(item)) {
+ catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
+ ME, killer, lost_exp);publish);
+ }
+ }
+ // jetzt den Rest.
+ limits[LIMIT_EVAL] = 80000;
+ foreach(object item: (environment() ? all_inventory(environment()) : ({}))
+ + deep_inventory(ME)
+ + (objectp(corpse) ? deep_inventory(corpse) : ({}))
+ - items ) {
+ // wenn nicht mehr genug Ticks, haben die restlichen Pech gehabt.
+ if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
+ break;
+ // NPD() koennen andere Objekt zerstoeren.
+ if (objectp(item)) {
+ catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
+ ME, killer, lost_exp);publish);
+ }
+ }
+
+ // Properties zuruecksetzen, sollte nach dem NotifyPlayerDeath()
+ // passieren, weil Objekte sich darin evtl. erstmal noch aufraeumen und
+ // props manipulieren.
+ reset_my_properties();
+ UpdateAttributes(); // Beim Tod werden Dinge entfernt, Attribute pruefen
+
+ // Auch Bewegung erst nach den NPD(), da diese den Spieler bewegen
+ // koennten, was eine Bewegung aus dem Todesraum waere, wenn die Bewegung
+ // vor dem NPD() stattfaende.
+ SetProp( P_GHOST, 1 ); // nach reset_my_properties() !
+ clone_object( "room/death/death_mark" )->move( ME, M_NOCHECK );
+
+ return 1;
+}
+
+
+public int AddHpHook( object ob )
+{
+ object *hooks;
+
+ if ( !objectp(ob) )
+ return 0;
+
+ if ( !pointerp(hooks = Query(P_HP_HOOKS)) ){
+ Set( P_HP_HOOKS, ({ ob }) );
+ return 1;
+ }
+
+ if ( member( hooks, ob ) >= 0 )
+ return 0;
+
+ Set( P_HP_HOOKS, (hooks - ({0})) + ({ ob }) );
+ return 1;
+}
+
+
+public int RemoveHpHook( object ob )
+{
+ object *hooks;
+
+ if ( !pointerp(hooks = Query(P_HP_HOOKS)) )
+ return 0;
+
+ Set( P_HP_HOOKS, hooks - ({ ob, 0 }) );
+ return 1;
+}
+
+
+static int _query_age() {
+ return Set(P_AGE, age, F_VALUE);
+}
+
+static int _set_hp( int hp )
+{
+ object *hooks;
+ int ret, i, old;
+
+ if ( (old = Query(P_HP, F_VALUE)) == hp )
+ return old;
+
+ ret = life::_set_hp(hp);
+
+ if ( ret == old )
+ return ret;
+
+ // Call old hooks in all objects... destructed objects will be ignored.
+ if (pointerp(hooks = Query(P_HP_HOOKS)))
+ call_other(hooks, "NotifyHpChange");
+
+ // Call new hooks.
+ HookFlow(H_HOOK_HP,ret);
+
+ // Report-ausgabe
+ status_report(DO_REPORT_HP, ret);
+
+ return ret;
+}
+
+
+static int _set_sp( int sp )
+{
+ object *hooks;
+ int ret, i, old;
+
+ if ( (old = Query(P_SP,F_VALUE)) == sp )
+ return old;
+
+ ret = life::_set_sp(sp);
+
+ if ( ret == old )
+ return ret;
+
+ // Call old hooks in all objects... destructed objects will be ignored.
+ if (pointerp(hooks = Query(P_HP_HOOKS)))
+ call_other(hooks, "NotifyHpChange");
+
+ // Call new hooks.
+ HookFlow(H_HOOK_SP,ret);
+
+ // Report-ausgabe
+ status_report(DO_REPORT_SP, ret);
+
+ return ret;
+}
+
+static int _set_poison(int n)
+{
+ int old = Query(P_POISON, F_VALUE);
+ if (old == n )
+ return old;
+ n = life::_set_poison(n);
+ if ( n == old )
+ return n;
+ // ggf. Statusreport ausgeben
+ if (interactive(ME))
+ status_report(DO_REPORT_POISON, n);
+ return n;
+}
+
+static int _set_max_poison(int n)
+{
+ if (n >= 0)
+ {
+ Set(P_MAX_POISON, n, F_VALUE);
+ int maxp=QueryProp(P_MAX_POISON); // koennte ne Querymethode drauf sein...
+ if (QueryProp(P_POISON) > maxp)
+ SetProp(P_POISON, maxp);
+ }
+ GMCP_Char( ([P_MAX_POISON: n]) );
+ return n;
+}
+
+static int _set_max_hp( int hp )
+{
+ if (hp >= 0)
+ {
+ Set(P_MAX_HP, hp, F_VALUE);
+ int maxhp=QueryProp(P_MAX_HP); // koennte ne Querymethode drauf sein...
+ if (QueryProp(P_HP) > maxhp)
+ SetProp(P_HP, maxhp);
+ }
+ GMCP_Char( ([P_MAX_HP: hp]) );
+ return hp;
+}
+
+static int _set_max_sp( int sp )
+{
+ if (sp >= 0)
+ {
+ Set(P_MAX_SP, sp, F_VALUE);
+ int maxsp=QueryProp(P_MAX_SP); // koennte ne Querymethode drauf sein...
+ if (QueryProp(P_SP) > maxsp)
+ SetProp(P_SP, maxsp);
+ }
+ GMCP_Char( ([P_MAX_SP: sp]) );
+ return sp;
+}
+
+static int _set_ghost( int g ) {
+ object team;
+
+ if(!g && query_hc_play()>1)
+ {
+ write("HAHAHA, DU BIST AUF EWIG MEIN.\n");
+ return Query(P_GHOST);
+ }
+
+ g = Set( P_GHOST, g );
+
+ if ( g && objectp(team = Query(P_TEAM)) )
+ team->RemoveMember(ME);
+
+ return g;
+}
+
+
+public int undie()
+{
+ mixed x, di;
+ mapping attr, mods;
+
+ if ( !this_interactive() || !previous_object() )
+ return 0;
+
+ if ( !IS_ARCH(this_interactive()) || !IS_ARCH(getuid(previous_object())) ||
+ process_call() )
+ log_file( "UNDIE", sprintf( "%s %O -> %O\n", dtime(time())[5..16],
+ this_interactive(), ME ) );
+
+ if ( x = Query(P_DEADS) )
+ x--;
+
+ Set( P_DEADS, x );
+
+ x = QueryProp(P_XP);
+
+ if ( (di = QueryProp(P_LEVEL)) < 20 || !IS_SEER(ME) )
+ x = (int)(x * 1.5);
+ else
+ // Umweg ueber float, weil bei hohen XP+Level sonst 32Bit nicht
+ // mehr ausreichen -> negative XP
+ x = (int) ( x * ((float) (di - 17) / (di - 18)) );
+
+ Set( P_XP, x );
+
+ attr = QueryProp(P_ATTRIBUTES) || ([]);
+ mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
+
+ if ( mappingp(di = QueryProp(P_DEATH_INFO)) ){
+ // Beim naechsten heart_beat checken
+ // Zesstra: Wieso eigentlich? Die Modifier werden doch direkt hier
+ // geloescht. So expired man auch einen Teil von nicht-undie-ten Toden
+ // vorzeitig...? Mal auskommentiert. 29.10.2007
+ //di[A_STR] = 1;
+ //di[A_DEX] = 1;
+ //di[A_INT] = 1;
+ //di[A_CON] = 1;
+ }
+ else
+ di = ([]);
+
+ mods[A_STR] = ((4 * (attr[A_STR] + mods[A_STR])) / 3) - attr[A_STR];
+ mods[A_DEX] = ((4 * (attr[A_DEX] + mods[A_DEX])) / 3) - attr[A_DEX];
+ mods[A_INT] = ((4 * (attr[A_INT] + mods[A_INT])) / 3) - attr[A_INT];
+ mods[A_CON] = ((4 * (attr[A_CON] + mods[A_CON])) / 3) - attr[A_CON];
+
+ if ( mods[A_STR] >= 0 ) {
+ m_delete( mods, A_STR );
+ m_delete( di, A_STR);
+ }
+ if ( mods[A_DEX] >= 0 ) {
+ m_delete( mods, A_DEX );
+ m_delete( di, A_DEX);
+ }
+ if ( mods[A_INT] >= 0 ) {
+ m_delete( mods, A_INT );
+ m_delete( di, A_INT);
+ }
+ if ( mods[A_CON] >= 0 ) {
+ m_delete( mods, A_CON );
+ m_delete( di, A_CON);
+ }
+
+ SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+ if (sizeof(di))
+ SetProp( P_DEATH_INFO, di );
+ else
+ SetProp( P_DEATH_INFO, 0);
+
+ suffer_time -= ((SUFFER_TIME)-1);
+
+ if ( suffer_time < 0 )
+ suffer_time = 0;
+
+ Set( P_GHOST, 0 );
+ return 1;
+}
+
+
+varargs public void die( int poisondeath, int extern)
+{
+ // laeuft schon ein die()? Fehler ausloesen, Ursache rekursiver die() soll
+ // gefunden werden. DINFO_EVAL_NUMBER wird in jedem Top-Level Call im
+ // driver erhoeht, d.h. gleiche Zahl signalisiert ein rekursives die().
+
+ if (die_in_progress == debug_info(DINFO_EVAL_NUMBER))
+ {
+ // TODO: ist das die_in_progress aus dem letzten Backend-Cycle?
+ raise_error(sprintf(
+ "die() in %O gerufen, aber die() laeuft bereits!\n",
+ this_object()));
+ }
+ die_in_progress = debug_info(DINFO_EVAL_NUMBER);
+
+ // Fuer HC-Player ists jetzt gelaufen...
+ if(query_hc_play()==1)
+ {
+ set_hc_play(capitalize(geteuid(ME)),time());
+ SetDefaultHome("/room/nirvana");
+ SetDefaultPrayRoom("/room/nirvana");
+ SetProp(P_START_HOME,"/room/nirvana");
+ log_file("HCDEAD",dtime(time())+" "+capitalize(geteuid(ME))
+ +" geht in das Nirvana ein!\n");
+ }
+
+ // Wenn das die() direkt von aussen gerufen wurde, muss P_KILLER hier
+ // gespeichert werden.
+ if (extern_call())
+ SetProp(P_KILLER, previous_object());
+
+ // Sichern der zu loeschenden Properties. Diese Props werden im Verlauf des
+ // Todes zurueckgesetzt. Einige Magier wollen diese Daten aber spaeter
+ // noch haben und fragen teilweise P_LAST_DEATH_PROPS im
+ // NotifyPlayerDeath() ab. Daher wird der Kram jetzt hier schonmal
+ // gesichert.
+ // BTW: Props mit Mappings/Arrays sollten kopiert werden.
+ SetProp(P_LAST_DEATH_PROPS, ([
+ P_POISON : QueryProp(P_POISON),
+ P_FROG : QueryProp(P_FROG),
+ P_ALCOHOL : QueryProp(P_ALCOHOL),
+ P_DRINK : QueryProp(P_DRINK),
+ P_FOOD : QueryProp(P_FOOD),
+ P_BLIND : QueryProp(P_BLIND),
+ P_DEAF : QueryProp(P_DEAF),
+ P_MAX_HANDS : QueryProp(P_MAX_HANDS),
+ P_PARA : QueryProp(P_PARA),
+ P_NO_REGENERATION : QueryProp(P_NO_REGENERATION),
+ P_HP : QueryProp(P_HP),
+ P_SP : QueryProp(P_SP),
+ P_LAST_DEATH_TIME : QueryProp(P_LAST_DEATH_TIME )
+ ]) );
+
+ // call the inherited die() with 10 Mio Ticks which will be accounted as 1
+ // Tick... ;-)
+ int *limits = query_limits();
+ limits[LIMIT_EVAL] == 10000000;
+ limits[LIMIT_COST] == LIMIT_UNLIMITED;
+ limited(#'::die, limits, poisondeath, (extern_call() ? 1 : 0));
+
+ // nach dem Tod sollte man auch keine LP mehr haben.
+ SetProp(P_HP, 0);
+
+ // naechster Tod kann kommen. Dekrementierung, da 0 ein gueltiger Wert
+ // fuer DINFO_EVAL_NUMBER waere. abs(), um nicht -__INT_MAX__ zu
+ // dekrementieren.
+ die_in_progress = abs(die_in_progress) - 1;
+}
+
+int defuel_food()
+{
+ int ret;
+ object prev;
+
+ ret=::defuel_food();
+ prev=previous_object();
+ if(!prev || !objectp(prev))
+ {
+ prev=this_object();
+ }
+
+ if(ret<=0)
+ {
+ call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,1);
+ }
+ else
+ {
+ call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,1);
+ }
+ return ret;
+}
+
+int defuel_drink()
+{
+ int ret;
+ object prev;
+
+ ret=::defuel_drink();
+ prev=previous_object();
+ if(!prev || !objectp(prev))
+ {
+ prev=this_object();
+ }
+
+ if(ret<=0)
+ {
+ call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,0);
+ }
+ else
+ {
+ call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,0);
+ }
+ return ret;
+}
+
+public void show_age()
+{ int i,j;
+
+ write("Alter:\t");
+ i = QueryProp(P_AGE);
+ if ((j=i/43200))
+ {
+ write(j + " Tag"+(j==1?" ":"e "));
+ i %= 43200;
+ }
+ if ((j=i/1800))
+ {
+ write(j + " Stunde"+(j==1?" ":"n "));
+ i %= 1800;
+ }
+ if ((j=i/30))
+ {
+ write(j + " Minute"+(j==1?" ":"n "));
+ i %= 30;
+ }
+ write(i*2 + " Sekunden.\n");
+}
+