ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/disease.c
Revision: 1.1.1.1 (vendor branch)
Committed: Fri Feb 3 07:14:30 2006 UTC (18 years, 3 months ago) by root
Content type: text/plain
Branch: UPSTREAM
CVS Tags: UPSTREAM_2006_03_15, LAST_C_VERSION, UPSTREAM_2006_02_22, difficulty_fix_merge_060810_2300, UPSTREAM_2006_02_03
Branch point for: difficulty_fix
Changes since 1.1: +0 -0 lines
Log Message:
initial import

File Contents

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