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