blob: e62b0150c936b1229f040ffb24863ddbe9b1f7c1 [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;
254 enemy->SetProp( P_KILLS, -1 );
255 }
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});
MG Mud User88f12472016-06-24 23:31:02 +0200417 if (environment() != killer)
418 items += ({environment()});
419 if (gi != killer && gi != environment())
420 items += ({gi});
421 foreach(object item: items) {
422 if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
423 break;
424 // falls ein NPD() implizit andere Objekt zerstoert hat.
425 if (objectp(item)) {
426 catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
427 ME, killer, lost_exp);publish);
428 }
429 }
430 // jetzt den Rest.
431 limits[LIMIT_EVAL] = 80000;
432 foreach(object item: (environment() ? all_inventory(environment()) : ({}))
433 + deep_inventory(ME)
434 + (objectp(corpse) ? deep_inventory(corpse) : ({}))
435 - items ) {
436 // wenn nicht mehr genug Ticks, haben die restlichen Pech gehabt.
437 if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
438 break;
439 // NPD() koennen andere Objekt zerstoeren.
440 if (objectp(item)) {
441 catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
442 ME, killer, lost_exp);publish);
443 }
444 }
445
446 // Properties zuruecksetzen, sollte nach dem NotifyPlayerDeath()
447 // passieren, weil Objekte sich darin evtl. erstmal noch aufraeumen und
448 // props manipulieren.
449 reset_my_properties();
450 UpdateAttributes(); // Beim Tod werden Dinge entfernt, Attribute pruefen
451
452 // Auch Bewegung erst nach den NPD(), da diese den Spieler bewegen
453 // koennten, was eine Bewegung aus dem Todesraum waere, wenn die Bewegung
454 // vor dem NPD() stattfaende.
455 SetProp( P_GHOST, 1 ); // nach reset_my_properties() !
456 clone_object( "room/death/death_mark" )->move( ME, M_NOCHECK );
457
458 return 1;
459}
460
461
462public int AddHpHook( object ob )
463{
464 object *hooks;
465
466 if ( !objectp(ob) )
467 return 0;
468
469 if ( !pointerp(hooks = Query(P_HP_HOOKS)) ){
470 Set( P_HP_HOOKS, ({ ob }) );
471 return 1;
472 }
473
474 if ( member( hooks, ob ) >= 0 )
475 return 0;
476
477 Set( P_HP_HOOKS, (hooks - ({0})) + ({ ob }) );
478 return 1;
479}
480
481
482public int RemoveHpHook( object ob )
483{
484 object *hooks;
485
486 if ( !pointerp(hooks = Query(P_HP_HOOKS)) )
487 return 0;
488
489 Set( P_HP_HOOKS, hooks - ({ ob, 0 }) );
490 return 1;
491}
492
493
494static int _query_age() {
495 return Set(P_AGE, age, F_VALUE);
496}
497
498static int _set_hp( int hp )
499{
500 object *hooks;
501 int ret, i, old;
502
503 if ( (old = Query(P_HP, F_VALUE)) == hp )
504 return old;
505
506 ret = life::_set_hp(hp);
507
508 if ( ret == old )
509 return ret;
510
511 // Call old hooks in all objects... destructed objects will be ignored.
512 if (pointerp(hooks = Query(P_HP_HOOKS)))
513 call_other(hooks, "NotifyHpChange");
514
515 // Call new hooks.
516 HookFlow(H_HOOK_HP,ret);
517
518 // Report-ausgabe
519 status_report(DO_REPORT_HP, ret);
520
521 return ret;
522}
523
524
525static int _set_sp( int sp )
526{
527 object *hooks;
528 int ret, i, old;
529
530 if ( (old = Query(P_SP,F_VALUE)) == sp )
531 return old;
532
533 ret = life::_set_sp(sp);
534
535 if ( ret == old )
536 return ret;
537
538 // Call old hooks in all objects... destructed objects will be ignored.
539 if (pointerp(hooks = Query(P_HP_HOOKS)))
540 call_other(hooks, "NotifyHpChange");
541
542 // Call new hooks.
543 HookFlow(H_HOOK_SP,ret);
544
545 // Report-ausgabe
546 status_report(DO_REPORT_SP, ret);
547
548 return ret;
549}
550
551static int _set_poison(int n)
552{
553 int old = Query(P_POISON, F_VALUE);
554 if (old == n )
555 return old;
556 n = life::_set_poison(n);
557 if ( n == old )
558 return n;
559 // ggf. Statusreport ausgeben
560 if (interactive(ME))
561 status_report(DO_REPORT_POISON, n);
562 return n;
563}
564
565static int _set_max_poison(int n)
566{
567 if (n >= 0)
568 {
569 Set(P_MAX_POISON, n, F_VALUE);
570 int maxp=QueryProp(P_MAX_POISON); // koennte ne Querymethode drauf sein...
571 if (QueryProp(P_POISON) > maxp)
572 SetProp(P_POISON, maxp);
573 }
574 GMCP_Char( ([P_MAX_POISON: n]) );
575 return n;
576}
577
578static int _set_max_hp( int hp )
579{
580 if (hp >= 0)
581 {
582 Set(P_MAX_HP, hp, F_VALUE);
583 int maxhp=QueryProp(P_MAX_HP); // koennte ne Querymethode drauf sein...
584 if (QueryProp(P_HP) > maxhp)
585 SetProp(P_HP, maxhp);
586 }
587 GMCP_Char( ([P_MAX_HP: hp]) );
588 return hp;
589}
590
591static int _set_max_sp( int sp )
592{
593 if (sp >= 0)
594 {
595 Set(P_MAX_SP, sp, F_VALUE);
596 int maxsp=QueryProp(P_MAX_SP); // koennte ne Querymethode drauf sein...
597 if (QueryProp(P_SP) > maxsp)
598 SetProp(P_SP, maxsp);
599 }
600 GMCP_Char( ([P_MAX_SP: sp]) );
601 return sp;
602}
603
604static int _set_ghost( int g ) {
605 object team;
606
607 if(!g && query_hc_play()>1)
608 {
609 write("HAHAHA, DU BIST AUF EWIG MEIN.\n");
610 return Query(P_GHOST);
611 }
612
613 g = Set( P_GHOST, g );
614
615 if ( g && objectp(team = Query(P_TEAM)) )
616 team->RemoveMember(ME);
617
618 return g;
619}
620
621
622public int undie()
623{
624 mixed x, di;
625 mapping attr, mods;
626
627 if ( !this_interactive() || !previous_object() )
628 return 0;
629
630 if ( !IS_ARCH(this_interactive()) || !IS_ARCH(getuid(previous_object())) ||
631 process_call() )
632 log_file( "UNDIE", sprintf( "%s %O -> %O\n", dtime(time())[5..16],
633 this_interactive(), ME ) );
634
635 if ( x = Query(P_DEADS) )
636 x--;
637
638 Set( P_DEADS, x );
639
640 x = QueryProp(P_XP);
641
642 if ( (di = QueryProp(P_LEVEL)) < 20 || !IS_SEER(ME) )
643 x = (int)(x * 1.5);
644 else
645 // Umweg ueber float, weil bei hohen XP+Level sonst 32Bit nicht
646 // mehr ausreichen -> negative XP
647 x = (int) ( x * ((float) (di - 17) / (di - 18)) );
648
649 Set( P_XP, x );
650
651 attr = QueryProp(P_ATTRIBUTES) || ([]);
652 mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
653
654 if ( mappingp(di = QueryProp(P_DEATH_INFO)) ){
655 // Beim naechsten heart_beat checken
656 // Zesstra: Wieso eigentlich? Die Modifier werden doch direkt hier
657 // geloescht. So expired man auch einen Teil von nicht-undie-ten Toden
658 // vorzeitig...? Mal auskommentiert. 29.10.2007
659 //di[A_STR] = 1;
660 //di[A_DEX] = 1;
661 //di[A_INT] = 1;
662 //di[A_CON] = 1;
663 }
664 else
665 di = ([]);
666
667 mods[A_STR] = ((4 * (attr[A_STR] + mods[A_STR])) / 3) - attr[A_STR];
668 mods[A_DEX] = ((4 * (attr[A_DEX] + mods[A_DEX])) / 3) - attr[A_DEX];
669 mods[A_INT] = ((4 * (attr[A_INT] + mods[A_INT])) / 3) - attr[A_INT];
670 mods[A_CON] = ((4 * (attr[A_CON] + mods[A_CON])) / 3) - attr[A_CON];
671
672 if ( mods[A_STR] >= 0 ) {
673 m_delete( mods, A_STR );
674 m_delete( di, A_STR);
675 }
676 if ( mods[A_DEX] >= 0 ) {
677 m_delete( mods, A_DEX );
678 m_delete( di, A_DEX);
679 }
680 if ( mods[A_INT] >= 0 ) {
681 m_delete( mods, A_INT );
682 m_delete( di, A_INT);
683 }
684 if ( mods[A_CON] >= 0 ) {
685 m_delete( mods, A_CON );
686 m_delete( di, A_CON);
687 }
688
689 SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
690 if (sizeof(di))
691 SetProp( P_DEATH_INFO, di );
692 else
693 SetProp( P_DEATH_INFO, 0);
694
695 suffer_time -= ((SUFFER_TIME)-1);
696
697 if ( suffer_time < 0 )
698 suffer_time = 0;
699
700 Set( P_GHOST, 0 );
701 return 1;
702}
703
704
705varargs public void die( int poisondeath, int extern)
706{
707 // laeuft schon ein die()? Fehler ausloesen, Ursache rekursiver die() soll
708 // gefunden werden. DINFO_EVAL_NUMBER wird in jedem Top-Level Call im
709 // driver erhoeht, d.h. gleiche Zahl signalisiert ein rekursives die().
710
711 if (die_in_progress == debug_info(DINFO_EVAL_NUMBER))
712 {
713 // TODO: ist das die_in_progress aus dem letzten Backend-Cycle?
714 raise_error(sprintf(
715 "die() in %O gerufen, aber die() laeuft bereits!\n",
716 this_object()));
717 }
718 die_in_progress = debug_info(DINFO_EVAL_NUMBER);
719
720 // Fuer HC-Player ists jetzt gelaufen...
721 if(query_hc_play()==1)
722 {
723 set_hc_play(capitalize(geteuid(ME)),time());
724 SetDefaultHome("/room/nirvana");
725 SetDefaultPrayRoom("/room/nirvana");
726 SetProp(P_START_HOME,"/room/nirvana");
727 log_file("HCDEAD",dtime(time())+" "+capitalize(geteuid(ME))
728 +" geht in das Nirvana ein!\n");
729 }
730
731 // Wenn das die() direkt von aussen gerufen wurde, muss P_KILLER hier
732 // gespeichert werden.
733 if (extern_call())
734 SetProp(P_KILLER, previous_object());
735
736 // Sichern der zu loeschenden Properties. Diese Props werden im Verlauf des
737 // Todes zurueckgesetzt. Einige Magier wollen diese Daten aber spaeter
738 // noch haben und fragen teilweise P_LAST_DEATH_PROPS im
739 // NotifyPlayerDeath() ab. Daher wird der Kram jetzt hier schonmal
740 // gesichert.
741 // BTW: Props mit Mappings/Arrays sollten kopiert werden.
742 SetProp(P_LAST_DEATH_PROPS, ([
743 P_POISON : QueryProp(P_POISON),
744 P_FROG : QueryProp(P_FROG),
745 P_ALCOHOL : QueryProp(P_ALCOHOL),
746 P_DRINK : QueryProp(P_DRINK),
747 P_FOOD : QueryProp(P_FOOD),
748 P_BLIND : QueryProp(P_BLIND),
749 P_DEAF : QueryProp(P_DEAF),
750 P_MAX_HANDS : QueryProp(P_MAX_HANDS),
751 P_PARA : QueryProp(P_PARA),
752 P_NO_REGENERATION : QueryProp(P_NO_REGENERATION),
753 P_HP : QueryProp(P_HP),
754 P_SP : QueryProp(P_SP),
755 P_LAST_DEATH_TIME : QueryProp(P_LAST_DEATH_TIME )
756 ]) );
757
758 // call the inherited die() with 10 Mio Ticks which will be accounted as 1
759 // Tick... ;-)
760 int *limits = query_limits();
761 limits[LIMIT_EVAL] == 10000000;
762 limits[LIMIT_COST] == LIMIT_UNLIMITED;
763 limited(#'::die, limits, poisondeath, (extern_call() ? 1 : 0));
764
765 // nach dem Tod sollte man auch keine LP mehr haben.
766 SetProp(P_HP, 0);
767
768 // naechster Tod kann kommen. Dekrementierung, da 0 ein gueltiger Wert
769 // fuer DINFO_EVAL_NUMBER waere. abs(), um nicht -__INT_MAX__ zu
770 // dekrementieren.
771 die_in_progress = abs(die_in_progress) - 1;
772}
773
774int defuel_food()
775{
776 int ret;
777 object prev;
778
779 ret=::defuel_food();
780 prev=previous_object();
781 if(!prev || !objectp(prev))
782 {
783 prev=this_object();
784 }
785
786 if(ret<=0)
787 {
788 call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,1);
789 }
790 else
791 {
792 call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,1);
793 }
794 return ret;
795}
796
797int defuel_drink()
798{
799 int ret;
800 object prev;
801
802 ret=::defuel_drink();
803 prev=previous_object();
804 if(!prev || !objectp(prev))
805 {
806 prev=this_object();
807 }
808
809 if(ret<=0)
810 {
811 call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,0);
812 }
813 else
814 {
815 call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,0);
816 }
817 return ret;
818}
819
820public void show_age()
821{ int i,j;
822
823 write("Alter:\t");
824 i = QueryProp(P_AGE);
825 if ((j=i/43200))
826 {
827 write(j + " Tag"+(j==1?" ":"e "));
828 i %= 43200;
829 }
830 if ((j=i/1800))
831 {
832 write(j + " Stunde"+(j==1?" ":"n "));
833 i %= 1800;
834 }
835 if ((j=i/30))
836 {
837 write(j + " Minute"+(j==1?" ":"n "));
838 i %= 30;
839 }
840 write(i*2 + " Sekunden.\n");
841}
842