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