ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/disease.C
Revision: 1.4
Committed: Sun Sep 10 15:59:57 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.3: +466 -378 lines
Log Message:
indent

File Contents

# Content
1
2 /*
3 * static char *rcsid_disease_c =
4 * "$Id: disease.C,v 1.3 2006-09-03 00:18:42 root Exp $";
5 */
6
7 /*
8 CrossFire, A Multiplayer game for X-windows
9
10 Copyright (C) 2002 Mark Wedel & Crossfire Development Team
11 Copyright (C) 1992 Frank Tore Johansen
12
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26
27 The authors can be reached via e-mail to crossfire-devel@real-time.com
28 */
29
30 /* This file contains all the code implementing diseases,
31 except for odds and ends in attack.c and in
32 living.c*/
33
34
35
36 /*
37
38 For DISEASES:
39 Stat Property Definition
40
41 attacktype Attack effects Attacktype of the disease. usu. AT_GODPOWER.
42 other_arch Creation object created and dropped when symptom moved.
43 title Message When the "disease" "infects" something, it will
44 print "title victim!!!" to the player who owns
45 the "disease".
46 wc+ Infectiousness How well the plague spreads person-to-person
47 magic+ Range range of infection
48 Stats* Disability What stats are reduced by the disease (str con...)
49 maxhp+ Persistence How long the disease can last OUTSIDE the host.
50 value TimeLeft Counter for persistence
51 dam^ Damage How much damage it does (%?).
52 maxgrace+ Duration How long before the disease is naturally cured.
53 food DurCount Counter for Duration
54
55 speed Speed How often the disease moves.
56 last_sp^ Lethargy Percentage of max speed--10 = 10% speed.
57
58 maxsp^ Mana deplete Saps mana.
59 ac^ Progressiveness How the diseases increases in severity.
60 last_eat*^ Deplete food saps food if negative
61 last_heal GrantImmunity If nonzero, disease does NOT grant immunity
62 when it runs out
63
64 exp experience experience awarded when plague cured
65 hp*^ ReduceRegen reduces regeneration of disease-bearer
66 sp*^ ReduceSpRegen reduces spellpoint regeneration
67
68 name Name Name of the plague
69 msg message What the plague says when it strikes.
70 race those affected species/race the plague strikes (* = everything)
71 level Plague Level General description of the plague's deadliness
72 armour Attenuation reduction in wc per generation of disease.
73 This builds in a self-limiting factor.
74
75
76 Explanations:
77 * means this # should be negative to cause adverse effect.
78 + means that this effect is modulated in spells by ldur
79 ^ means that this effect is modulated in spells by ldam
80
81 attacktype is the attacktype used by the disease to smite "dam" damage with.
82
83 wc/127 is the chance of someone in range catching it.
84
85 magic is the range at which infection may occur. If negative, the range is
86 NOT level dependent.
87
88 Stats are stat modifications. These should typically be negative.
89
90 maxhp is how long the disease will persist if the host dies and "drops" it,
91 in "disease moves", i.e., moves of the disease. If negative, permanent.
92
93
94 value is the counter for maxhp, it starts at maxhp and drops...
95
96 dam if positive, it is straight damage. if negative, a %-age.
97
98 maxgrace how long in "disease moves" the disease lasts in the host, if negative,
99 permanent until cured.
100
101 food if negative, disease is permanent. otherwise, decreases at <speed>,
102 disease goes away at food=0, set to "maxgrace" on infection.
103
104 speed is the speed of the disease, how fast "disease moves" occur.
105
106 last_sp is the lethargy imposed on the player by the disease. A lethargy
107 of "1" reduces the players speed to 1% of its normal value.
108
109 maxsp how much mana is sapped per "disease move". if negative, a %-age is
110 taken.
111
112 ac every "disease move" the severity of the symptoms are increased by
113 ac/100. (severity = 1 + (accumlated_progression)/100)
114
115 last_eat increases food usage if negative.
116
117
118
119 For SYMPTOMS:
120
121 Stats modify stats
122 hp modify regen
123 value progression counter (multiplier = value/100)
124 food modify food use (from last_eat in DISEASE)
125 maxsp suck mana ( as noted for DISEASE)
126 last_sp Lethargy
127 msg What to say
128 speed speed of movement, from DISEASE
129
130
131 */
132
133
134 #include <global.h>
135 #include <object.h>
136 #include <living.h>
137 #ifndef __CEXTRACT__
138 # include <sproto.h>
139 #endif
140 #include <spells.h>
141 #include <sounds.h>
142 #include <skills.h>
143
144 /* IMPLEMENTATION NOTES
145
146 Diseases may be contageous. They are objects which exist in a player's
147 inventory. They themselves do nothing, except modify Symptoms, or
148 spread to other live objects. Symptoms are what actually damage the player:
149 these are their own object. */
150
151 /* check if victim is susceptible to disease. */
152 static int
153 is_susceptible_to_disease (object *victim, object *disease)
154 {
155 if (!QUERY_FLAG (victim, FLAG_ALIVE))
156 return 0;
157
158 if (strstr (disease->race, "*") && !QUERY_FLAG (victim, FLAG_UNDEAD))
159 return 1;
160
161 if ((disease->race == undead_name) && QUERY_FLAG (victim, FLAG_UNDEAD))
162 return 1;
163
164 if ((victim->race && strstr (disease->race, victim->race)) || strstr (disease->race, victim->name))
165 return 1;
166
167 return 0;
168 }
169
170 int
171 move_disease (object *disease)
172 {
173 /* first task is to determine if the disease is inside or outside of someone.
174 * If outside, we decrement 'value' until we're gone.
175 */
176
177 if (disease->env == NULL)
178 { /* we're outside of someone */
179 if (disease->stats.maxhp > 0)
180 disease->value--;
181 if (disease->value == 0)
182 {
183 remove_ob (disease);
184 free_object (disease);
185 return 1;
186 }
187 }
188 else
189 {
190 /* if we're inside a person, have the disease run its course */
191 /* negative foods denote "perpetual" diseases. */
192 if (disease->stats.food > 0)
193 {
194 disease->stats.food--;
195 if (disease->stats.food == 0)
196 {
197 remove_symptoms (disease); /* remove the symptoms of this disease */
198 grant_immunity (disease);
199 remove_ob (disease);
200 free_object (disease);
201 return 1;
202 }
203 }
204 }
205 /* check to see if we infect others */
206 check_infection (disease);
207
208 /* impose or modify the symptoms of the disease */
209 if (disease->env && is_susceptible_to_disease (disease->env, disease))
210 do_symptoms (disease);
211
212 return 0;
213 }
214
215 /* remove any symptoms of disease
216 * Modified by MSW 2003-03-28 do try to find all the symptom the
217 * player may have - I think through some odd interactoins with
218 * disease level and player level and whatnot, a player could get
219 * more than one symtpom to a disease.
220 */
221
222 int
223 remove_symptoms (object *disease)
224 {
225 object *symptom, *victim = NULL;
226
227 while ((symptom = find_symptom (disease)) != NULL)
228 {
229 if (!victim)
230 victim = symptom->env;
231 remove_ob (symptom);
232 free_object (symptom);
233 }
234 if (victim)
235 fix_player (victim);
236 return 0;
237 }
238
239 /* argument is a disease */
240 object *
241 find_symptom (object *disease)
242 {
243 object *walk;
244
245 /* check the inventory for symptoms */
246 for (walk = disease->env->inv; walk; walk = walk->below)
247 if (!strcmp (walk->name, disease->name) && walk->type == SYMPTOM)
248 return walk;
249 return NULL;
250 }
251
252 /* searches around for more victims to infect */
253 int
254 check_infection (object *disease)
255 {
256 int x, y, range, mflags;
257 mapstruct *map, *map2;
258 object *tmp;
259 sint16 i, j, i2, j2;
260
261 range = abs (disease->magic);
262 if (disease->env)
263 {
264 x = disease->env->x;
265 y = disease->env->y;
266 map = disease->env->map;
267 }
268 else
269 {
270 x = disease->x;
271 y = disease->y;
272 map = disease->map;
273 }
274
275 if (map == NULL)
276 return 0;
277 for (i = x - range; i <= x + range; i++)
278 {
279 for (j = y - range; j <= y + range; j++)
280 {
281 mflags = get_map_flags (map, &map2, i, j, &i2, &j2);
282 if (!(mflags & P_OUT_OF_MAP) && (mflags & P_IS_ALIVE))
283 {
284 for (tmp = get_map_ob (map2, i2, j2); tmp; tmp = tmp->above)
285 {
286 infect_object (tmp, disease, 0);
287 }
288 }
289 }
290 }
291 return 1;
292 }
293
294
295 /* check to see if an object is infectable:
296 * objects with immunity aren't infectable.
297 * objects already infected aren't infectable.
298 * dead objects aren't infectable.
299 * undead objects are infectible only if specifically named.
300 */
301 int
302 infect_object (object *victim, object *disease, int force)
303 {
304 object *tmp;
305 object *new_disease;
306
307 /* don't infect inanimate objects */
308 if (!QUERY_FLAG (victim, FLAG_MONSTER) && !(victim->type == PLAYER))
309 return 0;
310
311 /* check and see if victim can catch disease: diseases
312 * are specific
313 */
314 if (!is_susceptible_to_disease (victim, disease))
315 return 0;
316
317 /* roll the dice on infection before doing the inventory check! */
318 if (!force && (random_roll (0, 126, victim, PREFER_HIGH) >= disease->stats.wc))
319 return 0;
320
321 /* do an immunity check */
322 if (victim->head)
323 tmp = victim->head->inv;
324 else
325 tmp = victim->inv;
326
327 /* There used to (IMO) be a flaw in the below - it used to be the case
328 * that if level check was done for both immunity and disease. This could
329 * result in a person with multiple afflictions of the same disease
330 * (eg, level 1 cold, level 2 cold, level 3 cold, etc), as long as
331 * they were cast in that same order. Instead, change it so that
332 * if you diseased, you can't get diseased more.
333 */
334
335 for ( /* tmp initialized in if, above */ ; tmp; tmp = tmp->below)
336 {
337 if (tmp->type == SIGN && !strcmp (tmp->name, disease->name) && tmp->level >= disease->level)
338 return 0; /*Immune! */
339 else if (tmp->type == DISEASE && !strcmp (tmp->name, disease->name))
340 return 0; /* already diseased */
341 }
342
343 /* If we've gotten this far, go ahead and infect the victim. */
344 new_disease = get_object ();
345 copy_object (disease, new_disease);
346 new_disease->stats.food = disease->stats.maxgrace;
347 new_disease->value = disease->stats.maxhp;
348 new_disease->stats.wc -= disease->last_grace; /* self-limiting factor */
349
350 /* Unfortunately, set_owner does the wrong thing to the skills pointers
351 * resulting in exp going into the owners *current* chosen skill.
352 */
353
354 if (get_owner (disease))
355 {
356 set_owner (new_disease, disease->owner);
357
358 /* Only need to update skill if different */
359 if (new_disease->skill != disease->skill)
360 new_disease->skill = disease->skill;
361 }
362 else
363 { /* for diseases which are passed by hitting, set owner and praying skill */
364 if (disease->env && disease->env->type == PLAYER)
365 {
366 object *player = disease->env;
367
368 set_owner (new_disease, player);
369
370 /* the skill pointer for these diseases should already be set up -
371 * hardcoding in 'praying' is not the right approach.
372 */
373 }
374 }
375
376 insert_ob_in_ob (new_disease, victim);
377 /* This appears to be a horrible case of overloading 'NO_PASS'
378 * for meaning in the diseases.
379 */
380 new_disease->move_block = 0;
381 if (new_disease->owner && new_disease->owner->type == PLAYER)
382 {
383 char buf[128];
384
385 /* if the disease has a title, it has a special infection message
386 * This messages is printed in the form MESSAGE victim
387 */
388 if (new_disease->title)
389 sprintf (buf, "%s %s!!", &disease->title, &victim->name);
390 else
391 sprintf (buf, "You infect %s with your disease, %s!", &victim->name, &new_disease->name);
392
393 if (victim->type == PLAYER)
394 new_draw_info (NDI_UNIQUE | NDI_RED, 0, new_disease->owner, buf);
395 else
396 new_draw_info (0, 4, new_disease->owner, buf);
397 }
398 if (victim->type == PLAYER)
399 new_draw_info (NDI_UNIQUE | NDI_RED, 0, victim, "You suddenly feel ill.");
400
401 return 1;
402
403 }
404
405
406
407 /* this function monitors the symptoms caused by the disease (if any),
408 causes symptoms, and modifies existing symptoms in the case of
409 existing diseases. */
410
411 int
412 do_symptoms (object *disease)
413 {
414 object *symptom;
415 object *victim;
416 object *tmp;
417
418 victim = disease->env;
419
420 /* This is a quick hack - for whatever reason, disease->env will point
421 * back to disease, causing endless loops. Why this happens really needs
422 * to be found, but this should at least prevent the infinite loops.
423 */
424
425 if (victim == NULL || victim == disease)
426 return 0; /* no-one to inflict symptoms on */
427
428 symptom = find_symptom (disease);
429 if (symptom == NULL)
430 {
431 /* no symptom? need to generate one! */
432 object *new_symptom;
433
434 /* first check and see if the carrier of the disease is immune. If so, no symptoms! */
435 if (!is_susceptible_to_disease (victim, disease))
436 return 0;
437
438 /* check for an actual immunity */
439 /* do an immunity check */
440 if (victim->head)
441 tmp = victim->head->inv;
442 else
443 tmp = victim->inv;
444
445 for ( /* tmp initialized in if, above */ ; tmp; tmp = tmp->below)
446 {
447 if (tmp->type == SIGN) /* possibly an immunity, or diseased */
448 if (!strcmp (tmp->name, disease->name) && tmp->level >= disease->level)
449 return 0; /*Immune! */
450 }
451
452 new_symptom = get_archetype (ARCH_SYMPTOM);
453
454 /* Something special done with dam. We want diseases to be more
455 * random in what they'll kill, so we'll make the damage they
456 * do random, note, this has a weird effect with progressive diseases.
457 */
458 if (disease->stats.dam != 0)
459 {
460 int dam = disease->stats.dam;
461
462 /* reduce the damage, on average, 50%, and making things random. */
463
464 dam = random_roll (1, FABS (dam), victim, PREFER_LOW);
465 if (disease->stats.dam < 0)
466 dam = -dam;
467 new_symptom->stats.dam = dam;
468 }
469
470
471 new_symptom->stats.maxsp = disease->stats.maxsp;
472 new_symptom->stats.food = new_symptom->stats.maxgrace;
473
474 new_symptom->name = new_symptom->name_pl = disease->name;
475
476 new_symptom->level = disease->level;
477 new_symptom->speed = disease->speed;
478 new_symptom->value = 0;
479 new_symptom->stats.Str = disease->stats.Str;
480 new_symptom->stats.Dex = disease->stats.Dex;
481 new_symptom->stats.Con = disease->stats.Con;
482 new_symptom->stats.Wis = disease->stats.Wis;
483 new_symptom->stats.Int = disease->stats.Int;
484 new_symptom->stats.Pow = disease->stats.Pow;
485 new_symptom->stats.Cha = disease->stats.Cha;
486 new_symptom->stats.sp = disease->stats.sp;
487 new_symptom->stats.food = disease->last_eat;
488 new_symptom->stats.maxsp = disease->stats.maxsp;
489 new_symptom->last_sp = disease->last_sp;
490 new_symptom->stats.exp = 0;
491 new_symptom->stats.hp = disease->stats.hp;
492 new_symptom->msg = disease->msg;
493 new_symptom->attacktype = disease->attacktype;
494 new_symptom->other_arch = disease->other_arch;
495
496 set_owner (new_symptom, disease->owner);
497
498 if (new_symptom->skill != disease->skill)
499 new_symptom->skill = disease->skill;
500
501 new_symptom->move_block = 0;
502 insert_ob_in_ob (new_symptom, victim);
503 return 1;
504 }
505
506 /* now deal with progressing diseases: we increase the debility
507 * caused by the symptoms.
508 */
509
510 if (disease->stats.ac != 0)
511 {
512 float scale;
513
514 symptom->value += disease->stats.ac;
515 scale = 1.0 + symptom->value / 100.0;
516 /* now rescale all the debilities */
517 symptom->stats.Str = (int) (scale * disease->stats.Str);
518 symptom->stats.Dex = (int) (scale * disease->stats.Dex);
519 symptom->stats.Con = (int) (scale * disease->stats.Con);
520 symptom->stats.Wis = (int) (scale * disease->stats.Wis);
521 symptom->stats.Int = (int) (scale * disease->stats.Int);
522 symptom->stats.Pow = (int) (scale * disease->stats.Pow);
523 symptom->stats.Cha = (int) (scale * disease->stats.Cha);
524 symptom->stats.dam = (int) (scale * disease->stats.dam);
525 symptom->stats.sp = (int) (scale * disease->stats.sp);
526 symptom->stats.food = (int) (scale * disease->last_eat);
527 symptom->stats.maxsp = (int) (scale * disease->stats.maxsp);
528 symptom->last_sp = (int) (scale * disease->last_sp);
529 symptom->stats.exp = 0;
530 symptom->stats.hp = (int) (scale * disease->stats.hp);
531 symptom->msg = disease->msg;
532 symptom->attacktype = disease->attacktype;
533 symptom->other_arch = disease->other_arch;
534 }
535 SET_FLAG (symptom, FLAG_APPLIED);
536 fix_player (victim);
537 return 1;
538 }
539
540
541 /* grants immunity to plagues we've seen before. */
542 int
543 grant_immunity (object *disease)
544 {
545 object *immunity;
546 object *walk;
547
548 /* Don't give immunity to this disease if last_heal is set. */
549 if (disease->last_heal)
550 return 0;
551 /* first, search for an immunity of the same name */
552 for (walk = disease->env->inv; walk; walk = walk->below)
553 {
554 if (walk->type == 98 && !strcmp (disease->name, walk->name))
555 {
556 walk->level = disease->level;
557 return 1; /* just update the existing immunity. */
558 }
559 }
560 immunity = get_archetype ("immunity");
561 immunity->name = disease->name;
562 immunity->level = disease->level;
563 immunity->move_block = 0;
564 insert_ob_in_ob (immunity, disease->env);
565 return 1;
566
567 }
568
569
570 /* make the symptom do the nasty things it does */
571
572 int
573 move_symptom (object *symptom)
574 {
575 object *victim = symptom->env;
576 object *new_ob;
577 int sp_reduce;
578
579 if (victim == NULL || victim->map == NULL)
580 { /* outside a monster/player, die immediately */
581 remove_ob (symptom);
582 free_object (symptom);
583 return 0;
584 }
585
586 if (symptom->stats.dam > 0)
587 hit_player (victim, symptom->stats.dam, symptom, symptom->attacktype, 1);
588 else
589 hit_player (victim, (int) MAX (1, -victim->stats.maxhp * symptom->stats.dam / 100.0), symptom, symptom->attacktype, 1);
590
591 if (symptom->stats.maxsp > 0)
592 sp_reduce = symptom->stats.maxsp;
593 else
594 sp_reduce = (int) MAX (1, victim->stats.maxsp * symptom->stats.maxsp / 100.0);
595 victim->stats.sp = MAX (0, victim->stats.sp - sp_reduce);
596
597 /* create the symptom "other arch" object and drop it here
598 * under every part of the monster
599 * The victim may well have died.
600 */
601
602 if (victim->map == NULL)
603 return 0;
604 if (symptom->other_arch)
605 {
606 object *tmp;
607
608 tmp = victim;
609 if (tmp->head != NULL)
610 tmp = tmp->head;
611 for ( /*tmp initialized above */ ; tmp != NULL; tmp = tmp->more)
612 {
613 new_ob = arch_to_object (symptom->other_arch);
614 new_ob->x = tmp->x;
615 new_ob->y = tmp->y;
616 new_ob->map = victim->map;
617 insert_ob_in_map (new_ob, victim->map, victim, 0);
618 }
619 }
620 new_draw_info (NDI_UNIQUE | NDI_RED, 0, victim, symptom->msg);
621
622 return 1;
623 }
624
625
626 /* possibly infect due to direct physical contact
627 i.e., AT_PHYSICAL-- called from "hit_player_attacktype" */
628
629 int
630 check_physically_infect (object *victim, object *hitter)
631 {
632 object *walk;
633
634 /* search for diseases, give every disease a chance to infect */
635 for (walk = hitter->inv; walk != NULL; walk = walk->below)
636 if (walk->type == DISEASE)
637 infect_object (victim, walk, 0);
638 return 1;
639 }
640
641 /* find a disease in someone*/
642 object *
643 find_disease (object *victim)
644 {
645 object *walk;
646
647 for (walk = victim->inv; walk; walk = walk->below)
648 if (walk->type == DISEASE)
649 return walk;
650 return NULL;
651 }
652
653 /* do the cure disease stuff, from the spell "cure disease" */
654
655 int
656 cure_disease (object *sufferer, object *caster)
657 {
658 object *disease, *next;
659 int casting_level;
660 int cure = 0;
661
662 if (caster)
663 casting_level = caster->level;
664 else
665 casting_level = 1000; /* if null caster, CURE all. */
666
667 for (disease = sufferer->inv; disease; disease = next)
668 {
669 next = disease->below;
670
671 if (disease->type == DISEASE)
672 { /* attempt to cure this disease */
673 /* If caster lvel is higher than disease level, cure chance
674 * is automatic. If lower, then the chance is basically
675 * 1 in level_diff - if there is a 5 level difference, chance
676 * is 1 in 5.
677 */
678 if ((casting_level >= disease->level) || (!(random_roll (0, (disease->level - casting_level - 1), caster, PREFER_LOW))))
679 {
680
681 remove_symptoms (disease);
682 remove_ob (disease);
683 cure = 1;
684 if (caster)
685 change_exp (caster, disease->stats.exp, caster->chosen_skill ? &caster->chosen_skill->skill : (const char *) 0, 0);
686 free_object (disease);
687 }
688 }
689 }
690 if (cure)
691 {
692 /* Only draw these messages once */
693 if (caster)
694 new_draw_info_format (NDI_UNIQUE, 0, caster, "You cure a disease!");
695 new_draw_info (NDI_UNIQUE, 0, sufferer, "You no longer feel diseased.");
696 }
697 return 1;
698 }
699
700 /* reduces disease progression: reduce_symptoms
701 * return true if we actually reduce a disease.
702 */
703
704 int
705 reduce_symptoms (object *sufferer, int reduction)
706 {
707 object *walk;
708 int success = 0;
709
710 for (walk = sufferer->inv; walk; walk = walk->below)
711 {
712 if (walk->type == SYMPTOM)
713 {
714 if (walk->value > 0)
715 {
716 success = 1;
717 walk->value = MAX (0, walk->value - 2 * reduction);
718 /* give the disease time to modify this symptom,
719 * and reduce its severity. */
720 walk->speed_left = 0;
721 }
722 }
723 }
724 if (success)
725 new_draw_info (NDI_UNIQUE, 0, sufferer, "Your illness seems less severe.");
726 return success;
727 }