ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/disease.C
Revision: 1.63
Committed: Fri Mar 26 01:04:44 2010 UTC (14 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.62: +1 -1 lines
Log Message:
update copyright for up to 2010

File Contents

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