blob: 3a52dee32dbccf043c90d7f4304addaeb2442f68 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// player/life.c -- player life handling
4//
5// $Id: life.c 9397 2015-12-11 20:29:26Z Zesstra $
6
7// Defines some things which are different than in living.c
8// One example is the heart_beat().
9
10// Properties
11// P_AGE -- Age
12
13#pragma strong_types, save_types, rtt_checks
14#pragma range_check
15#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +020016
17inherit "/std/living/life";
18
19#include <rtlimits.h>
20#include <debug_info.h>
21
22#define NEED_PROTOTYPES
23#include <thing/properties.h>
24#include <player/base.h>
25#include <player/life.h>
26#include <moving.h>
27#include <player/comm.h>
28#include <player/gmcp.h>
29#undef NEED_PROTOTYPES
30
31#include <config.h>
32#include <wizlevels.h>
33#include <defines.h>
34#include <language.h>
35#include <properties.h>
36#include <hook.h>
37
38#include <living/life.h>
39#include <player/pklog.h>
40#include <player/combat.h>
41#include <health.h>
42#include <living/combat.h>
43#include <attributes.h>
44#include <defuel.h>
45#include <new_skills.h>
46
47// Die Folgen eines Todes wirken 4 Stunden lang nach
48#define SUFFER_TIME 7200
49#define P_DEATH_INFO "death_info"
50
51int age; /* Number of heart beats of this character. */
52
53private int suffer_time;
54static int time_to_save; /* when to next save player */
55private nosave int die_in_progress; // semaphore fuer die()-Aufrufe.
56
57static int _set_playerkills( int val );
58
59protected void create()
60{
61 ::create();
62 Set(P_AGE, -1, F_SET_METHOD);
63 Set(P_AGE, PROTECTED, F_MODE);
64 Set( P_KILLS, SAVE|SECURED, F_MODE_AS );
65 Set( P_GHOST, SAVE, F_MODE_AS );
66 Set( P_TIMING_MAP, SAVE|SECURED, F_MODE_AS );
67 Set( P_LAST_DEATH_TIME, SAVE|SECURED, F_MODE_AS );
68 Set( P_DEATH_INFO, SAVE|SECURED, F_MODE_AS );
69 Set( P_DEFUEL_LIMIT_FOOD,PROTECTED,F_MODE_AS);
70 Set( P_DEFUEL_LIMIT_DRINK,PROTECTED,F_MODE_AS);
71 Set( P_DEFUEL_TIME_FOOD,PROTECTED,F_MODE_AS);
72 Set( P_DEFUEL_TIME_DRINK,PROTECTED,F_MODE_AS);
73 Set( P_DEFUEL_AMOUNT_FOOD,PROTECTED,F_MODE_AS);
74 Set( P_DEFUEL_AMOUNT_DRINK,PROTECTED,F_MODE_AS);
75 offerHook(H_HOOK_HP,1);
76 offerHook(H_HOOK_SP,1);
77 // P_TIMING_MAP aufraeumen, aber zeitverzoegert, weil jetzt die Daten noch
78 // nicht aus dem Savefile geladen wurden.
79 call_out(#'expire_timing_map, 4);
80}
81
82// called from base.c in Reconnect()
83protected void reconnect() {
84 expire_timing_map();
85}
86
87static int _set_playerkills( int val )
88{
89 string tmp;
90 int playerkills;
91
92 // ist der Setzer in einer Arena/Schattenwelt. Dann nicht. (Ja, Bug ist,
93 // dass EMs aus Schattenwelt/Arena heraus auch das PK-Flag nicht
94 // zuruecksetzen koennen.)
95 if ( previous_object(1) && environment(previous_object(1)) &&
96 (tmp = object_name(environment(previous_object(1)))) &&
97 CheckArenaFight(previous_object(1)) )
98 return 0;
99
100 tmp = sprintf( "%O: %s %O %s",
101 previous_object(1) || this_interactive() || this_player(),
102 getuid(ME), val, dtime(time()) );
103
104 playerkills = Query(P_KILLS);
105
106 if( intp(val) && val >= 0 )
107 if( previous_object(1) && IS_ARCH(getuid(previous_object(1))) &&
108 this_interactive() && IS_ARCH(this_interactive()) )
109 playerkills = val;
110 else
111 tmp += " ILLEGAL!";
112 else
113 playerkills++;
114
115 if ( !previous_object(1) || !query_once_interactive(previous_object(1)) ||
116 IS_LEARNER(previous_object(1)) )
117 log_file( "SET_KILLS", tmp + "\n" );
118
119 return Set( P_KILLS, playerkills );
120}
121
122
123public int death_suffering()
124{
125 if ( suffer_time <= 0 )
126 return suffer_time = 0;
127
128 return 1 + (10 * suffer_time) / SUFFER_TIME;
129}
130
131
132protected void heart_beat()
133{
134 mapping di, mods;
135
136 ++age; // erstmal altern ;-)
137
138 ::heart_beat();
139
140 if ( age > time_to_save ){
141 save_me(1);
142 time_to_save = age + 500;
143 }
144
145 // als geist hat man mit den meisten weltlichen Dingen nicht allzuviel zu
146 // tun.
147 if ( QueryProp(P_GHOST) )
148 return;
149
150 if ( suffer_time > 0 )
151 suffer_time--;
152
153 // Todesfolgen nur alle 20 Sekunden (10 HB) checken.
154 // Das ist immer noch oft genug und spart Rechenzeit.
155 if ( (age % 10) || !mappingp(di = QueryProp(P_DEATH_INFO)) )
156 return;
157
158 mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"];
159 if (!mappingp(mods)) return;
160
161 if ( mods[A_STR] && --di[A_STR] <= 0) {
162 // einen Attributspunkt regenerieren
163 if ( mods[A_STR] < -1 ) {
164 mods[A_STR]++;
165 di[A_STR] = (110 + 5 * (di[A_STR, 1] + mods[A_STR])) / 10;
166 }
167 else {
168 m_delete( mods, A_STR );
169 m_delete( di, A_STR );
170 }
171 }
172
173 if ( mods[A_CON] && --di[A_CON] <= 0) {
174 // einen Attributspunkt regenerieren
175 if ( mods[A_CON] < -1 ){
176 mods[A_CON]++;
177 di[A_CON] = (110 + 5 * (di[A_CON, 1] + mods[A_CON])) / 10;
178 }
179 else {
180 m_delete( mods, A_CON );
181 m_delete( di, A_CON );
182 }
183 }
184
185 if ( mods[A_DEX] && --di[A_DEX] <= 0) {
186 // einen Attributspunkt regenerieren
187 if ( mods[A_DEX] < -1 ){
188 mods[A_DEX]++;
189 di[A_DEX] = (110 + 5 * (di[A_DEX, 1] + mods[A_DEX])) / 10;
190 }
191 else {
192 m_delete( mods, A_DEX );
193 m_delete( di, A_DEX );
194 }
195 }
196
197 if ( mods[A_INT] && --di[A_INT] <= 0) {
198 // einen Attributspunkt regenerieren
199 if ( mods[A_INT] < -1 ){
200 mods[A_INT]++;
201 di[A_INT] = (110 + 5 * (di[A_INT, 1] + mods[A_INT])) / 10;
202 }
203 else {
204 m_delete( mods, A_INT );
205 m_delete( di, A_INT );
206 }
207 }
208
209 if ( sizeof(di) && sizeof(mods))
210 SetProp( P_DEATH_INFO, di );
211 else
212 SetProp( P_DEATH_INFO, 0 );
213
214 SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
215}
216
217
218public void force_save() {
219 time_to_save = 0;
220}
221
222
223nomask public int do_damage( int dam, object enemy )
224{
225 int hit_point;
226
227 if( QueryProp(P_GHOST) || dam <= 0 )
228 return 0;
229
230 hit_point = QueryProp(P_HP);
231
232 if ( query_once_interactive(ME) && dam >= hit_point && IS_LEARNING(ME) ){
233 tell_object( ME, "Deine magischen Kraefte verhindern Deinen Tod.\n" );
234 return 0;
235 }
236
237 if ( !objectp(enemy) )
238 enemy = previous_object() || this_interactive() || this_player();
239
240 hit_point -= dam;
241
242 if( hit_point < 0 ){
243 if ( !interactive(ME) )
244 // Netztote sterben nicht
245 hit_point = 10;
246 else {
247 if ( objectp(enemy) && interactive( enemy ) && enemy != ME &&
248 !QueryProp(P_TESTPLAYER) && !IS_WIZARD(ME) &&
249 !CheckArenaFight(ME) ) {
250 if ( QueryPlAttacked(enemy) )
251 hit_point = 1;
252 else {
253 hit_point = 0;
Zesstra5d4336d2020-04-18 20:37:58 +0200254 enemy->SetProp( P_KILLS, -1 ); // wert ist egal
MG Mud User88f12472016-06-24 23:31:02 +0200255 }
256
257 log_file( "KILLER",
258 sprintf( "%s %s(%d/%d) toetete %s(%d/%d)%s\n",
259 ctime(time()),
260 getuid(enemy), query_wiz_level(enemy),
Arathornea00e142019-12-03 00:29:27 +0100261 enemy->QueryProp(P_LEVEL), getuid(ME),
MG Mud User88f12472016-06-24 23:31:02 +0200262 query_wiz_level(ME), QueryProp(P_LEVEL),
263 (hit_point ? " NOTWEHR=>KEIN PK" : "") ) );
264 }
265 else {
266 string killername;
267 if (objectp(enemy))
268 killername=sprintf("%s (%s)",
269 BLUE_NAME(enemy), REAL_UID(enemy));
270 else
271 killername="??? (???)";
272
273 if ( !QueryProp(P_TESTPLAYER) )
274 create_kill_log_entry(killername, enemy );
275 }
276
277 if ( enemy )
278 enemy->StopHuntFor( ME, 1 );
279
280 map_objects( QueryEnemies()[0], "StopHuntFor", ME, 1 );
281 StopHuntingMode(1);
282
283 Set( P_KILLER, enemy );
284 die();
285 }
286 }
287
288 SetProp( P_HP, hit_point );
289 return dam;
290}
291
292
293// Loescht im sterbenden Spieler die 'koerperabhaengigen' Properties
294private void reset_my_properties()
295{
296 // Loeschen der Properties
297 if ( QueryProp(P_POISON) )
298 {
299 // Don't die twice 'cause of the same poison
300 Set( P_POISON, 0, F_SET_METHOD );
301 Set( P_POISON, 0, F_QUERY_METHOD );
302 SetProp( P_POISON, 0 );
303 }
304
305 Set( P_FROG, 0, F_QUERY_METHOD );
306 Set( P_FROG, 0, F_SET_METHOD );
307 SetProp( P_FROG, 0 ); // Damit die Attribute auch stimmen.
308 Set( P_ALCOHOL, 0, F_QUERY_METHOD );
309 Set( P_ALCOHOL, 0, F_SET_METHOD );
310 SetProp(P_ALCOHOL, 0 );
311 Set( P_DRINK, 0, F_QUERY_METHOD );
312 Set( P_DRINK, 0, F_SET_METHOD );
313 SetProp(P_DRINK, 0 );
314 Set( P_FOOD, 0, F_QUERY_METHOD );
315 Set( P_FOOD, 0, F_SET_METHOD );
316 SetProp(P_FOOD, 0 );
317 Set( P_BLIND, 0, F_QUERY_METHOD );
318 Set( P_BLIND, 0, F_SET_METHOD );
319 SetProp(P_BLIND, 0 );
320 Set( P_DEAF, 0, F_QUERY_METHOD );
321 Set( P_DEAF, 0, F_SET_METHOD );
322 SetProp(P_DEAF, 0 );
323 Set( P_MAX_HANDS, 0, F_QUERY_METHOD );
324 Set( P_MAX_HANDS, 0, F_SET_METHOD );
325 SetProp( P_MAX_HANDS, 2 );
326 Set( P_HANDS_USED_BY, 0, F_QUERY_METHOD );
327 Set( P_HANDS_USED_BY, 0, F_SET_METHOD );
328 SetProp( P_HANDS_USED_BY, ({}) );
329 Set( P_PARA, 0 );
330 Set( P_NO_REGENERATION, 0, F_QUERY_METHOD );
331 Set( P_NO_REGENERATION, 0, F_SET_METHOD );
332 SetProp(P_NO_REGENERATION, 0 );
333 Set( P_TMP_MOVE_HOOK, 0, F_QUERY_METHOD );
334 Set( P_TMP_MOVE_HOOK, 0, F_SET_METHOD );
335 SetProp(P_TMP_MOVE_HOOK, 0 );
336 Set( P_LAST_DEATH_TIME , time() );
337 // damit der Teddy o.ae. mitbekommt, dass man jetzt tot ist ]:->
338 SetProp( P_HP, 0 );
339 SetProp( P_SP, 0 );
340}
341
342varargs protected int second_life( object corpse )
343{
344 int lost_exp, level;
345 // Es gibt Funktionen, die sollte man nicht per Hand aufrufen duerfen ;-)
346 if ( extern_call() && previous_object() != ME )
347 return 0;
348
349 if ( query_once_interactive(ME) && IS_LEARNING(ME) ){
350 tell_object( ME, "Sei froh, dass Du unsterblich bist, sonst waere "
351 "es eben zu Ende gewesen.\n" );
352 return 1;
353 }
354
355 if ( !IS_SEER(ME) || (level = QueryProp(P_LEVEL)) < 20 )
356 lost_exp = QueryProp(P_XP) / 3;
357 else
358 lost_exp = QueryProp(P_XP) / (level - 17);
359
360 AddExp(-lost_exp);
361
362
363 // Todesfolgen setzen....
364 //SetProp( P_DEATH_INFO, 1);
365 if ( !IS_LEARNING(ME) && !QueryProp(P_TESTPLAYER) ) {
366
367 mapping attr = QueryProp(P_ATTRIBUTES);
368 mapping mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
369
370 // Attribute auf 75% vom aktuellen Wert senken
371 mods[A_STR] = -attr[A_STR] + (3 * (attr[A_STR] + mods[A_STR]) / 4);
372 mods[A_CON] = -attr[A_CON] + (3 * (attr[A_CON] + mods[A_CON]) / 4);
373 mods[A_DEX] = -attr[A_DEX] + (3 * (attr[A_DEX] + mods[A_DEX]) / 4);
374 mods[A_INT] = -attr[A_INT] + (3 * (attr[A_INT] + mods[A_INT]) / 4);
375
376 SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
377
378 int offs = 220; // 220 heart_beats == 7min20
379 // Die 220 HB sind fix, dazu kommen noch 5 HB pro realem
380 // Attributspunkt. Geteilt wird das ganze noch durch 10, weil im HB
381 // nur alle 10 HBs die TF gecheckt werden. Da wird dann alle 10 HB ein
382 // Punkt abgezogen und wenn 0 erreicht ist, wird das Attribut um eins
383 // regeneriert.
384 SetProp( P_DEATH_INFO, ([
385 A_STR: (offs + 5 * (attr[A_STR] + mods[A_STR]))/10; attr[A_STR],
386 A_CON: (offs + 5 * (attr[A_CON] + mods[A_CON]))/10; attr[A_CON],
387 A_DEX: (offs + 5 * (attr[A_DEX] + mods[A_DEX]))/10; attr[A_DEX],
388 A_INT: (offs + 5 * (attr[A_INT] + mods[A_INT]))/10; attr[A_INT]
389 ]) );
390
391 // die suffer_time wird via death_suffering() von
392 // QuerySkillAttribute() abgefragt und geht dann als Malus in
393 // SA_QUALITY mit ein.
394 if ( suffer_time <= 2*SUFFER_TIME )
395 suffer_time += SUFFER_TIME - 1;
396 else
397 suffer_time = 3 * SUFFER_TIME - 1;
398 }
399
400 // Die verschiedenen NotifyPlayerDeath-Funktionen koennen u.U. schlecht
401 // programmiert sein und zuviel Rechenzeit ziehen. Deshalb werden sie mit
402 // einem Limits von 150k bzw. 80k aufgerufen. Ausserdem werden sie nur
403 // gerufen, solange noch min. 25k Ticks da sind.
404 int *limits=query_limits();
405 limits[LIMIT_EVAL] = 150000;
406 limits[LIMIT_COST] = LIMIT_DEFAULT;
407
Arathorn1f2172d2019-12-03 00:30:17 +0100408 string|object killer = QueryProp(P_KILLER);
409 string|object gi = QueryProp(P_GUILD);
MG Mud User88f12472016-06-24 23:31:02 +0200410 if (stringp(gi))
411 gi = find_object("/gilden/"+gi);
412 // jedes Objekt nur einmal, aber nicht via m_indices(mkmapping)) wegen
413 // Verlust der Reihenfolge.
Arathorn1f2172d2019-12-03 00:30:17 +0100414 object* items = ({});
415 if (objectp(killer))
416 items = ({killer});
Zesstra8bf98802020-04-04 16:18:47 +0200417 else
418 {
419 // (Fast) alle NotifyPlayerDeath() gehen davon aus, dass <killer> nur
420 // ein Objekt ist. Daher wird im Falle von Gifttoden - wenn <killer>
421 // ein string ist, immer 0 uebergehen.
422 killer = 0;
423 }
MG Mud User88f12472016-06-24 23:31:02 +0200424 if (environment() != killer)
425 items += ({environment()});
426 if (gi != killer && gi != environment())
427 items += ({gi});
428 foreach(object item: items) {
429 if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
430 break;
431 // falls ein NPD() implizit andere Objekt zerstoert hat.
432 if (objectp(item)) {
433 catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
434 ME, killer, lost_exp);publish);
435 }
436 }
437 // jetzt den Rest.
438 limits[LIMIT_EVAL] = 80000;
439 foreach(object item: (environment() ? all_inventory(environment()) : ({}))
440 + deep_inventory(ME)
441 + (objectp(corpse) ? deep_inventory(corpse) : ({}))
442 - items ) {
443 // wenn nicht mehr genug Ticks, haben die restlichen Pech gehabt.
444 if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
445 break;
446 // NPD() koennen andere Objekt zerstoeren.
447 if (objectp(item)) {
448 catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
449 ME, killer, lost_exp);publish);
450 }
451 }
452
453 // Properties zuruecksetzen, sollte nach dem NotifyPlayerDeath()
454 // passieren, weil Objekte sich darin evtl. erstmal noch aufraeumen und
455 // props manipulieren.
456 reset_my_properties();
457 UpdateAttributes(); // Beim Tod werden Dinge entfernt, Attribute pruefen
458
459 // Auch Bewegung erst nach den NPD(), da diese den Spieler bewegen
460 // koennten, was eine Bewegung aus dem Todesraum waere, wenn die Bewegung
461 // vor dem NPD() stattfaende.
462 SetProp( P_GHOST, 1 ); // nach reset_my_properties() !
463 clone_object( "room/death/death_mark" )->move( ME, M_NOCHECK );
464
465 return 1;
466}
467
468
469public int AddHpHook( object ob )
470{
471 object *hooks;
472
473 if ( !objectp(ob) )
474 return 0;
475
476 if ( !pointerp(hooks = Query(P_HP_HOOKS)) ){
477 Set( P_HP_HOOKS, ({ ob }) );
478 return 1;
479 }
480
481 if ( member( hooks, ob ) >= 0 )
482 return 0;
483
484 Set( P_HP_HOOKS, (hooks - ({0})) + ({ ob }) );
485 return 1;
486}
487
488
489public int RemoveHpHook( object ob )
490{
491 object *hooks;
492
493 if ( !pointerp(hooks = Query(P_HP_HOOKS)) )
494 return 0;
495
496 Set( P_HP_HOOKS, hooks - ({ ob, 0 }) );
497 return 1;
498}
499
500
501static int _query_age() {
502 return Set(P_AGE, age, F_VALUE);
503}
504
505static int _set_hp( int hp )
506{
507 object *hooks;
508 int ret, i, old;
509
510 if ( (old = Query(P_HP, F_VALUE)) == hp )
511 return old;
512
513 ret = life::_set_hp(hp);
514
515 if ( ret == old )
516 return ret;
517
518 // Call old hooks in all objects... destructed objects will be ignored.
519 if (pointerp(hooks = Query(P_HP_HOOKS)))
520 call_other(hooks, "NotifyHpChange");
521
522 // Call new hooks.
523 HookFlow(H_HOOK_HP,ret);
524
525 // Report-ausgabe
526 status_report(DO_REPORT_HP, ret);
527
528 return ret;
529}
530
531
532static int _set_sp( int sp )
533{
534 object *hooks;
535 int ret, i, old;
536
537 if ( (old = Query(P_SP,F_VALUE)) == sp )
538 return old;
539
540 ret = life::_set_sp(sp);
541
542 if ( ret == old )
543 return ret;
544
545 // Call old hooks in all objects... destructed objects will be ignored.
546 if (pointerp(hooks = Query(P_HP_HOOKS)))
547 call_other(hooks, "NotifyHpChange");
548
549 // Call new hooks.
550 HookFlow(H_HOOK_SP,ret);
551
552 // Report-ausgabe
553 status_report(DO_REPORT_SP, ret);
554
555 return ret;
556}
557
558static int _set_poison(int n)
559{
560 int old = Query(P_POISON, F_VALUE);
561 if (old == n )
562 return old;
563 n = life::_set_poison(n);
564 if ( n == old )
565 return n;
566 // ggf. Statusreport ausgeben
567 if (interactive(ME))
568 status_report(DO_REPORT_POISON, n);
569 return n;
570}
571
572static int _set_max_poison(int n)
573{
574 if (n >= 0)
575 {
576 Set(P_MAX_POISON, n, F_VALUE);
577 int maxp=QueryProp(P_MAX_POISON); // koennte ne Querymethode drauf sein...
578 if (QueryProp(P_POISON) > maxp)
579 SetProp(P_POISON, maxp);
580 }
581 GMCP_Char( ([P_MAX_POISON: n]) );
582 return n;
583}
584
585static int _set_max_hp( int hp )
586{
587 if (hp >= 0)
588 {
589 Set(P_MAX_HP, hp, F_VALUE);
590 int maxhp=QueryProp(P_MAX_HP); // koennte ne Querymethode drauf sein...
591 if (QueryProp(P_HP) > maxhp)
592 SetProp(P_HP, maxhp);
593 }
594 GMCP_Char( ([P_MAX_HP: hp]) );
595 return hp;
596}
597
598static int _set_max_sp( int sp )
599{
600 if (sp >= 0)
601 {
602 Set(P_MAX_SP, sp, F_VALUE);
603 int maxsp=QueryProp(P_MAX_SP); // koennte ne Querymethode drauf sein...
604 if (QueryProp(P_SP) > maxsp)
605 SetProp(P_SP, maxsp);
606 }
607 GMCP_Char( ([P_MAX_SP: sp]) );
608 return sp;
609}
610
611static int _set_ghost( int g ) {
612 object team;
613
614 if(!g && query_hc_play()>1)
615 {
616 write("HAHAHA, DU BIST AUF EWIG MEIN.\n");
617 return Query(P_GHOST);
618 }
619
620 g = Set( P_GHOST, g );
621
622 if ( g && objectp(team = Query(P_TEAM)) )
623 team->RemoveMember(ME);
624
625 return g;
626}
627
628
629public int undie()
630{
631 mixed x, di;
632 mapping attr, mods;
633
634 if ( !this_interactive() || !previous_object() )
635 return 0;
636
637 if ( !IS_ARCH(this_interactive()) || !IS_ARCH(getuid(previous_object())) ||
638 process_call() )
639 log_file( "UNDIE", sprintf( "%s %O -> %O\n", dtime(time())[5..16],
640 this_interactive(), ME ) );
641
642 if ( x = Query(P_DEADS) )
643 x--;
644
645 Set( P_DEADS, x );
646
647 x = QueryProp(P_XP);
648
649 if ( (di = QueryProp(P_LEVEL)) < 20 || !IS_SEER(ME) )
650 x = (int)(x * 1.5);
651 else
652 // Umweg ueber float, weil bei hohen XP+Level sonst 32Bit nicht
653 // mehr ausreichen -> negative XP
654 x = (int) ( x * ((float) (di - 17) / (di - 18)) );
655
656 Set( P_XP, x );
657
658 attr = QueryProp(P_ATTRIBUTES) || ([]);
659 mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
660
661 if ( mappingp(di = QueryProp(P_DEATH_INFO)) ){
662 // Beim naechsten heart_beat checken
663 // Zesstra: Wieso eigentlich? Die Modifier werden doch direkt hier
664 // geloescht. So expired man auch einen Teil von nicht-undie-ten Toden
665 // vorzeitig...? Mal auskommentiert. 29.10.2007
666 //di[A_STR] = 1;
667 //di[A_DEX] = 1;
668 //di[A_INT] = 1;
669 //di[A_CON] = 1;
670 }
671 else
672 di = ([]);
673
674 mods[A_STR] = ((4 * (attr[A_STR] + mods[A_STR])) / 3) - attr[A_STR];
675 mods[A_DEX] = ((4 * (attr[A_DEX] + mods[A_DEX])) / 3) - attr[A_DEX];
676 mods[A_INT] = ((4 * (attr[A_INT] + mods[A_INT])) / 3) - attr[A_INT];
677 mods[A_CON] = ((4 * (attr[A_CON] + mods[A_CON])) / 3) - attr[A_CON];
678
679 if ( mods[A_STR] >= 0 ) {
680 m_delete( mods, A_STR );
681 m_delete( di, A_STR);
682 }
683 if ( mods[A_DEX] >= 0 ) {
684 m_delete( mods, A_DEX );
685 m_delete( di, A_DEX);
686 }
687 if ( mods[A_INT] >= 0 ) {
688 m_delete( mods, A_INT );
689 m_delete( di, A_INT);
690 }
691 if ( mods[A_CON] >= 0 ) {
692 m_delete( mods, A_CON );
693 m_delete( di, A_CON);
694 }
695
696 SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
697 if (sizeof(di))
698 SetProp( P_DEATH_INFO, di );
699 else
700 SetProp( P_DEATH_INFO, 0);
701
702 suffer_time -= ((SUFFER_TIME)-1);
703
704 if ( suffer_time < 0 )
705 suffer_time = 0;
706
707 Set( P_GHOST, 0 );
708 return 1;
709}
710
711
712varargs public void die( int poisondeath, int extern)
713{
714 // laeuft schon ein die()? Fehler ausloesen, Ursache rekursiver die() soll
715 // gefunden werden. DINFO_EVAL_NUMBER wird in jedem Top-Level Call im
716 // driver erhoeht, d.h. gleiche Zahl signalisiert ein rekursives die().
717
718 if (die_in_progress == debug_info(DINFO_EVAL_NUMBER))
719 {
720 // TODO: ist das die_in_progress aus dem letzten Backend-Cycle?
721 raise_error(sprintf(
722 "die() in %O gerufen, aber die() laeuft bereits!\n",
723 this_object()));
724 }
725 die_in_progress = debug_info(DINFO_EVAL_NUMBER);
726
727 // Fuer HC-Player ists jetzt gelaufen...
728 if(query_hc_play()==1)
729 {
730 set_hc_play(capitalize(geteuid(ME)),time());
731 SetDefaultHome("/room/nirvana");
732 SetDefaultPrayRoom("/room/nirvana");
733 SetProp(P_START_HOME,"/room/nirvana");
734 log_file("HCDEAD",dtime(time())+" "+capitalize(geteuid(ME))
735 +" geht in das Nirvana ein!\n");
736 }
737
738 // Wenn das die() direkt von aussen gerufen wurde, muss P_KILLER hier
739 // gespeichert werden.
740 if (extern_call())
741 SetProp(P_KILLER, previous_object());
742
743 // Sichern der zu loeschenden Properties. Diese Props werden im Verlauf des
744 // Todes zurueckgesetzt. Einige Magier wollen diese Daten aber spaeter
745 // noch haben und fragen teilweise P_LAST_DEATH_PROPS im
746 // NotifyPlayerDeath() ab. Daher wird der Kram jetzt hier schonmal
747 // gesichert.
748 // BTW: Props mit Mappings/Arrays sollten kopiert werden.
749 SetProp(P_LAST_DEATH_PROPS, ([
750 P_POISON : QueryProp(P_POISON),
751 P_FROG : QueryProp(P_FROG),
752 P_ALCOHOL : QueryProp(P_ALCOHOL),
753 P_DRINK : QueryProp(P_DRINK),
754 P_FOOD : QueryProp(P_FOOD),
755 P_BLIND : QueryProp(P_BLIND),
756 P_DEAF : QueryProp(P_DEAF),
757 P_MAX_HANDS : QueryProp(P_MAX_HANDS),
758 P_PARA : QueryProp(P_PARA),
759 P_NO_REGENERATION : QueryProp(P_NO_REGENERATION),
760 P_HP : QueryProp(P_HP),
761 P_SP : QueryProp(P_SP),
762 P_LAST_DEATH_TIME : QueryProp(P_LAST_DEATH_TIME )
763 ]) );
764
765 // call the inherited die() with 10 Mio Ticks which will be accounted as 1
766 // Tick... ;-)
767 int *limits = query_limits();
768 limits[LIMIT_EVAL] == 10000000;
769 limits[LIMIT_COST] == LIMIT_UNLIMITED;
770 limited(#'::die, limits, poisondeath, (extern_call() ? 1 : 0));
771
772 // nach dem Tod sollte man auch keine LP mehr haben.
773 SetProp(P_HP, 0);
774
775 // naechster Tod kann kommen. Dekrementierung, da 0 ein gueltiger Wert
776 // fuer DINFO_EVAL_NUMBER waere. abs(), um nicht -__INT_MAX__ zu
777 // dekrementieren.
778 die_in_progress = abs(die_in_progress) - 1;
779}
780
781int defuel_food()
782{
783 int ret;
784 object prev;
785
786 ret=::defuel_food();
787 prev=previous_object();
788 if(!prev || !objectp(prev))
789 {
790 prev=this_object();
791 }
792
793 if(ret<=0)
794 {
795 call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,1);
796 }
797 else
798 {
799 call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,1);
800 }
801 return ret;
802}
803
804int defuel_drink()
805{
806 int ret;
807 object prev;
808
809 ret=::defuel_drink();
810 prev=previous_object();
811 if(!prev || !objectp(prev))
812 {
813 prev=this_object();
814 }
815
816 if(ret<=0)
817 {
818 call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,0);
819 }
820 else
821 {
822 call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,0);
823 }
824 return ret;
825}
826
827public void show_age()
828{ int i,j;
829
830 write("Alter:\t");
831 i = QueryProp(P_AGE);
832 if ((j=i/43200))
833 {
834 write(j + " Tag"+(j==1?" ":"e "));
835 i %= 43200;
836 }
837 if ((j=i/1800))
838 {
839 write(j + " Stunde"+(j==1?" ":"n "));
840 i %= 1800;
841 }
842 if ((j=i/30))
843 {
844 write(j + " Minute"+(j==1?" ":"n "));
845 i %= 30;
846 }
847 write(i*2 + " Sekunden.\n");
848}
849