blob: 73f1ff79b77b894380c5e48b13c0fe2ae1a6f1d5 [file] [log] [blame]
// 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");
}