Crossfire Server, Branch 1.12  R12190
disease.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_disease_c =
00003  *   "$Id: disease.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 /*
00006     CrossFire, A Multiplayer game for X-windows
00007 
00008     Copyright (C) 2006 Mark Wedel & Crossfire Development Team
00009     Copyright (C) 1992 Frank Tore Johansen
00010 
00011     This program is free software; you can redistribute it and/or modify
00012     it under the terms of the GNU General Public License as published by
00013     the Free Software Foundation; either version 2 of the License, or
00014     (at your option) any later version.
00015 
00016     This program is distributed in the hope that it will be useful,
00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019     GNU General Public License for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with this program; if not, write to the Free Software
00023     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00024 
00025     The authors can be reached via e-mail to crossfire-devel@real-time.com
00026 */
00027 
00034 /*
00035  * For DISEASES:
00036  * Stat            Property        Definition
00037  *
00038  * attacktype      Attack effects  Attacktype of the disease. usu. AT_GODPOWER.
00039  * other_arch      Creation        object created and dropped when symptom moved.
00040  * title           Message         When the "disease" "infects" something, it will
00041  *                                 print "title victim!!!" to the player who owns
00042  *                                 the "disease".
00043  * wc+             Infectiousness  How well the plague spreads person-to-person
00044  * magic+          Range           range of infection
00045  * Stats*          Disability      What stats are reduced by the disease (str con...)
00046  * maxhp+          Persistence     How long the disease can last OUTSIDE the host.
00047  *  * value           TimeLeft        Counter for persistence
00048  * dam^            Damage          How much damage it does (%?).
00049  * maxgrace+       Duration        How long before the disease is naturally cured.
00050  * food            DurCount        Counter for Duration
00051  *
00052  * speed           Speed           How often the disease moves.
00053  * last_sp^        Lethargy        Percentage of max speed--10 = 10% speed.
00054  *
00055  * maxsp^          Mana deplete    Saps mana.
00056  * ac^             Progressiveness How the diseases increases in severity.
00057  * last_eat*^      Deplete food    saps food if negative
00058  * last_heal       GrantImmunity   If nonzero, disease does NOT grant immunity
00059  *                                 when it runs out
00060  *
00061  * exp             experience      experience awarded when plague cured
00062  * hp*^            ReduceRegen     reduces regeneration of disease-bearer
00063  * sp*^            ReduceSpRegen   reduces spellpoint regeneration
00064  *
00065  * name            Name            Name of the plague
00066  * msg             message         What the plague says when it strikes.
00067  * race            those affected  species/race the plague strikes (* = everything)
00068  * level           Plague Level    General description of the plague's deadliness
00069  * armour          Attenuation     reduction in wc per generation of disease.
00070  *                                 This builds in a self-limiting factor.
00071  *
00072  *
00073  * Explanations:
00074  * * means this # should be negative to cause adverse effect.
00075  * + means that this effect is modulated in spells by ldur
00076  * ^ means that this effect is modulated in spells by ldam
00077  *
00078  * attacktype is the attacktype used by the disease to smite "dam" damage with.
00079  *
00080  * wc/127 is the chance of someone in range catching it.
00081  *
00082  * magic is the range at which infection may occur.  If negative, the range is
00083  * NOT level dependent.
00084  *
00085  * Stats are stat modifications.  These should typically be negative.
00086  *
00087  * maxhp is how long the disease will persist if the host dies and "drops" it,
00088  *       in "disease moves", i.e., moves of the disease.  If negative, permanent.
00089  *
00090  * value is the counter for maxhp, it starts at maxhp and drops...
00091  *
00092  * dam     if positive, it is straight damage.  if negative, a %-age.
00093  *
00094  * maxgrace  how long in "disease moves" the disease lasts in the host, if negative,
00095  *           permanent until cured.
00096  *
00097  * food    if negative, disease is permanent.  otherwise, decreases at <speed>,
00098  *         disease goes away at food=0, set to "maxgrace" on infection.
00099  *
00100  * speed is the speed of the disease, how fast "disease moves" occur.
00101  *
00102  * last_sp is the lethargy imposed on the player by the disease.  A lethargy
00103  *        of "1" reduces the players speed to 1% of its normal value.
00104  *
00105  * maxsp how much mana is sapped per "disease move".  if negative, a %-age is
00106  *      taken.
00107  *
00108  * ac  every "disease move" the severity of the symptoms are increased by
00109  *     ac/100.  (severity = 1 + (accumlated_progression)/100)
00110  *
00111  * last_eat  increases food usage if negative.
00112  *
00113  *
00114  *
00115  * For SYMPTOMS:
00116  *
00117  * Stats            modify stats
00118  * hp               modify regen
00119  * value            progression counter (multiplier = value/100)
00120  * food             modify food use (from last_eat in DISEASE)
00121  * maxsp            suck mana ( as noted for DISEASE)
00122  * last_sp          Lethargy
00123  * msg              What to say
00124  * speed            speed of movement, from DISEASE
00125  */
00126 
00127 #include <global.h>
00128 #include <object.h>
00129 #include <living.h>
00130 #ifndef __CEXTRACT__
00131 #include <sproto.h>
00132 #endif
00133 #include <spells.h>
00134 #include <sounds.h>
00135 #include <skills.h>
00136 #include <assert.h>
00137 
00138 static void remove_symptoms(object *disease);
00139 static object *find_symptom(object *disease);
00140 static void check_infection(object *disease);
00141 static void do_symptoms(object *disease);
00142 static void grant_immunity(object *disease);
00143 
00144 
00145 
00146 /*  IMPLEMENTATION NOTES
00147 
00148 Diseases may be contageous.  They are objects which exist in a player's
00149 inventory.  They themselves do nothing, except modify Symptoms, or
00150 spread to other live objects.  Symptoms are what actually damage the player:
00151 these are their own object. */
00152 
00165 static int is_susceptible_to_disease(object *victim, object *disease) {
00166     /* Non living and DMs are immune. */
00167     if (!QUERY_FLAG(victim, FLAG_ALIVE) || QUERY_FLAG(victim, FLAG_WIZ))
00168         return 0;
00169 
00170     if (strstr(disease->race, "*") && !QUERY_FLAG(victim, FLAG_UNDEAD))
00171         return 1;
00172 
00173     if ((disease->race == undead_name) && QUERY_FLAG(victim, FLAG_UNDEAD))
00174         return 1;
00175 
00176     if ((victim->race && strstr(disease->race, victim->race))
00177     || strstr(disease->race, victim->name))
00178         return 1;
00179 
00180     return 0;
00181 }
00182 
00192 int move_disease(object *disease) {
00193     /*  first task is to determine if the disease is inside or outside of someone.
00194      * If outside, we decrement 'value' until we're gone.
00195      */
00196 
00197     /* DMs don't infect, and don't suffer either. */
00198     if (disease->env && QUERY_FLAG(disease->env, FLAG_WIZ))
00199         return 0;
00200 
00201     if (disease->env == NULL) { /* we're outside of someone */
00202         if (disease->stats.maxhp > 0)
00203             disease->value--;
00204         if (disease->value == 0) {
00205             remove_ob(disease);
00206             free_object(disease);
00207             return 1;
00208         }
00209     } else {
00210         /* if we're inside a person, have the disease run its course */
00211         /* negative foods denote "perpetual" diseases. */
00212         if (disease->stats.food > 0) {
00213             disease->stats.food--;
00214             if (disease->stats.food == 0) {
00215                 remove_symptoms(disease);  /* remove the symptoms of this disease */
00216                 grant_immunity(disease);
00217                 remove_ob(disease);
00218                 free_object(disease);
00219                 return 1;
00220             }
00221         }
00222     }
00223     /*  check to see if we infect others */
00224     check_infection(disease);
00225 
00226     /* impose or modify the symptoms of the disease */
00227     if (disease->env && is_susceptible_to_disease(disease->env, disease))
00228         do_symptoms(disease);
00229 
00230     return 0;
00231 }
00232 
00244 static void remove_symptoms(object *disease) {
00245     object *symptom, *victim = NULL;
00246 
00247     assert(disease != NULL);
00248 
00249     while ((symptom = find_symptom(disease)) != NULL) {
00250         if (!victim)
00251             victim = symptom->env;
00252         remove_ob(symptom);
00253         free_object(symptom);
00254     }
00255     if (victim)
00256         fix_object(victim);
00257 }
00258 
00267 static object *find_symptom(object *disease) {
00268     object *walk;
00269 
00270     assert(disease->env != NULL);
00271 
00272     /* check the inventory for symptoms */
00273     for (walk = disease->env->inv; walk; walk = walk->below)
00274         if (!strcmp(walk->name, disease->name) && walk->type == SYMPTOM)
00275             return walk;
00276     return NULL;
00277 }
00278 
00285 static void check_infection(object *disease) {
00286     int x, y, range, mflags;
00287     mapstruct *map, *map2;
00288     object *tmp;
00289     sint16 i, j, i2, j2;
00290 
00291     range = abs(disease->magic);
00292     if (disease->env) {
00293         x = disease->env->x;
00294         y = disease->env->y;
00295         map = disease->env->map;
00296     } else {
00297         x = disease->x;
00298         y = disease->y;
00299         map = disease->map;
00300     }
00301 
00302     if (map == NULL)
00303         return;
00304     for (i = x-range; i <= x+range; i++) {
00305         for (j = y-range; j <= y+range; j++) {
00306             mflags = get_map_flags(map, &map2, i, j, &i2, &j2);
00307             if (!(mflags&P_OUT_OF_MAP) && (mflags&P_IS_ALIVE)) {
00308                 for (tmp = GET_MAP_OB(map2, i2, j2); tmp; tmp = tmp->above) {
00309                     infect_object(tmp, disease, 0);
00310                 }
00311             }
00312         }
00313     }
00314     return;
00315 }
00316 
00336 int infect_object(object *victim, object *disease, int force) {
00337     object *tmp;
00338     object *new_disease;
00339 
00340     /* don't infect inanimate objects */
00341     if (!QUERY_FLAG(victim, FLAG_MONSTER) && !(victim->type == PLAYER))
00342         return 0;
00343 
00344     /* check and see if victim can catch disease:  diseases
00345      *  are specific
00346      */
00347     if (!is_susceptible_to_disease(victim, disease))
00348         return 0;
00349 
00350     /* If disease is on battleground, only infect other victims on battleground.
00351        Not checking results in spectators being infected, which could lead to PK. */
00352     if ((disease->map && op_on_battleground(disease, NULL, NULL, NULL))
00353     || (disease->env && op_on_battleground(disease->env, NULL, NULL, NULL)))
00354         if (!op_on_battleground(victim, NULL, NULL, NULL))
00355             return 0;
00356 
00357     /* roll the dice on infection before doing the inventory check!  */
00358     if (!force && (random_roll(0, 126, victim, PREFER_HIGH) >= disease->stats.wc))
00359         return 0;
00360 
00361     /* do an immunity check */
00362     if (victim->head)
00363         tmp = victim->head->inv;
00364     else
00365         tmp = victim->inv;
00366 
00367     /* There used to (IMO) be a flaw in the below - it used to be the case
00368      * that if level check was done for both immunity and disease. This could
00369      * result in a person with multiple afflictions of the same disease
00370      * (eg, level 1 cold, level 2 cold, level 3 cold, etc), as long as
00371      * they were cast in that same order.  Instead, change it so that
00372      * if you diseased, you can't get diseased more.
00373      */
00374 
00375     for (/* tmp initialized in if, above */; tmp; tmp = tmp->below) {
00376         if (tmp->type == SIGN && !strcmp(tmp->name, disease->name) && tmp->level >= disease->level)
00377             return 0;  /*Immune! */
00378         else if (tmp->type == DISEASE && !strcmp(tmp->name, disease->name))
00379             return 0; /* already diseased */
00380     }
00381 
00382     /*  If we've gotten this far, go ahead and infect the victim.  */
00383     new_disease = get_object();
00384     copy_object(disease, new_disease);
00385     new_disease->stats.food = disease->stats.maxgrace;
00386     new_disease->value = disease->stats.maxhp;
00387     new_disease->stats.wc -= disease->last_grace;  /* self-limiting factor */
00388 
00389     /* Unfortunately, set_owner does the wrong thing to the skills pointers
00390      *  resulting in exp going into the owners *current *chosen skill.
00391      */
00392 
00393     if (get_owner(disease)) {
00394         set_owner(new_disease, disease->owner);
00395 
00396         /* Only need to update skill if different */
00397         if (new_disease->skill != disease->skill) {
00398             if (new_disease->skill)
00399                 free_string(new_disease->skill);
00400             if (disease->skill)
00401                 new_disease->skill = add_refcount(disease->skill);
00402         }
00403     } else { /* for diseases which are passed by hitting, set owner and praying skill*/
00404         if (disease->env && disease->env->type == PLAYER) {
00405             object *player = disease->env;
00406 
00407             set_owner(new_disease, player);
00408             /* the skill pointer for these diseases should already be set up -
00409              * hardcoding in 'praying' is not the right approach.
00410              */
00411         }
00412     }
00413 
00414     insert_ob_in_ob(new_disease, victim);
00415     /* This appears to be a horrible case of overloading 'NO_PASS'
00416      * for meaning in the diseases.
00417      */
00418     new_disease->move_block = 0;
00419     if (new_disease->owner && new_disease->owner->type == PLAYER) {
00420         char buf[128];
00421 
00422         /* if the disease has a title, it has a special infection message
00423          * This messages is printed in the form MESSAGE victim
00424          */
00425         if (new_disease->title)
00426             snprintf(buf, sizeof(buf), "%s %s!!", disease->title, victim->name);
00427         else
00428             snprintf(buf, sizeof(buf), "You infect %s with your disease, %s!", victim->name, new_disease->name);
00429 
00430         if (victim->type == PLAYER)
00431             draw_ext_info(NDI_UNIQUE|NDI_RED, 0, new_disease->owner, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_DID_HIT,
00432                           buf, buf);
00433         else
00434             draw_ext_info(0, 4, new_disease->owner, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_DID_HIT,
00435                           buf, buf);
00436     }
00437     if (victim->type == PLAYER)
00438         draw_ext_info(NDI_UNIQUE|NDI_RED, 0, victim, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START,
00439                       "You suddenly feel ill.", NULL);
00440 
00441     return 1;
00442 }
00443 
00452 static void do_symptoms(object *disease) {
00453     object *symptom;
00454     object *victim;
00455     object *tmp;
00456     victim = disease->env;
00457 
00458     /* This is a quick hack - for whatever reason, disease->env will point
00459      * back to disease, causing endless loops.  Why this happens really needs
00460      * to be found, but this should at least prevent the infinite loops.
00461      */
00462 
00463     if (victim == NULL || victim == disease)
00464         return;/* no-one to inflict symptoms on */
00465 
00466     /* DMs don't suffer from diseases. */
00467     if (QUERY_FLAG(victim, FLAG_WIZ))
00468         return;
00469 
00470     symptom = find_symptom(disease);
00471     if (symptom == NULL) {
00472         /* no symptom?  need to generate one! */
00473         object *new_symptom;
00474 
00475         /* first check and see if the carrier of the disease is immune.  If so, no symptoms!  */
00476         if (!is_susceptible_to_disease(victim, disease))
00477             return;
00478 
00479         /* check for an actual immunity */
00480         /* do an immunity check */
00481         if (victim->head)
00482             tmp = victim->head->inv;
00483         else
00484             tmp = victim->inv;
00485 
00486         for (/* tmp initialized in if, above */; tmp; tmp = tmp->below) {
00487             if (tmp->type == SIGN) /* possibly an immunity, or diseased*/
00488                 if (!strcmp(tmp->name, disease->name) && tmp->level >= disease->level)
00489                     return;  /*Immune! */
00490         }
00491 
00492         new_symptom = create_archetype(ARCH_SYMPTOM);
00493 
00494         /* Something special done with dam.  We want diseases to be more
00495          * random in what they'll kill, so we'll make the damage they
00496          * do random, note, this has a weird effect with progressive diseases.
00497          */
00498         if (disease->stats.dam != 0) {
00499             int dam = disease->stats.dam;
00500 
00501             /* reduce the damage, on average, 50%, and making things random. */
00502 
00503             dam = random_roll(1, FABS(dam), victim, PREFER_LOW);
00504             if (disease->stats.dam < 0)
00505                 dam = -dam;
00506             new_symptom->stats.dam = dam;
00507         }
00508 
00509         new_symptom->stats.maxsp = disease->stats.maxsp;
00510         new_symptom->stats.food = new_symptom->stats.maxgrace;
00511 
00512         FREE_AND_COPY(new_symptom->name, disease->name);
00513         FREE_AND_COPY(new_symptom->name_pl, disease->name);
00514         new_symptom->level = disease->level;
00515         new_symptom->speed = disease->speed;
00516         new_symptom->value = 0;
00517         new_symptom->stats.Str = disease->stats.Str;
00518         new_symptom->stats.Dex = disease->stats.Dex;
00519         new_symptom->stats.Con = disease->stats.Con;
00520         new_symptom->stats.Wis = disease->stats.Wis;
00521         new_symptom->stats.Int = disease->stats.Int;
00522         new_symptom->stats.Pow = disease->stats.Pow;
00523         new_symptom->stats.Cha = disease->stats.Cha;
00524         new_symptom->stats.sp  = disease->stats.sp;
00525         new_symptom->stats.food = disease->last_eat;
00526         new_symptom->stats.maxsp = disease->stats.maxsp;
00527         new_symptom->last_sp = disease->last_sp;
00528         new_symptom->stats.exp = 0;
00529         new_symptom->stats.hp = disease->stats.hp;
00530         new_symptom->msg = add_string(disease->msg);
00531         new_symptom->attacktype = disease->attacktype;
00532         new_symptom->other_arch = disease->other_arch;
00533 
00534         set_owner(new_symptom, disease->owner);
00535         if (new_symptom->skill != disease->skill) {
00536             if (new_symptom->skill)
00537                 free_string(new_symptom->skill);
00538             if (disease->skill)
00539                 new_symptom->skill = add_refcount(disease->skill);
00540         }
00541         new_symptom->move_block = 0;
00542         insert_ob_in_ob(new_symptom, victim);
00543         return;
00544     }
00545 
00546     /* now deal with progressing diseases:  we increase the debility
00547      * caused by the symptoms.
00548      */
00549 
00550     if (disease->stats.ac != 0) {
00551         float scale;
00552 
00553         symptom->value += disease->stats.ac;
00554         scale = 1.0+symptom->value/100.0;
00555         /* now rescale all the debilities */
00556         symptom->stats.Str = (int)(scale*disease->stats.Str);
00557         symptom->stats.Dex = (int)(scale*disease->stats.Dex);
00558         symptom->stats.Con = (int)(scale*disease->stats.Con);
00559         symptom->stats.Wis = (int)(scale*disease->stats.Wis);
00560         symptom->stats.Int = (int)(scale*disease->stats.Int);
00561         symptom->stats.Pow = (int)(scale*disease->stats.Pow);
00562         symptom->stats.Cha = (int)(scale*disease->stats.Cha);
00563         symptom->stats.dam = (int)(scale*disease->stats.dam);
00564         symptom->stats.sp = (int)(scale*disease->stats.sp);
00565         symptom->stats.food = (int)(scale*disease->last_eat);
00566         symptom->stats.maxsp = (int)(scale*disease->stats.maxsp);
00567         symptom->last_sp = (int)(scale*disease->last_sp);
00568         symptom->stats.exp = 0;
00569         symptom->stats.hp = (int)(scale*disease->stats.hp);
00570         symptom->msg = add_string(disease->msg);
00571         symptom->attacktype = disease->attacktype;
00572         symptom->other_arch = disease->other_arch;
00573     }
00574     SET_FLAG(symptom, FLAG_APPLIED);
00575     fix_object(victim);
00576 }
00577 
00584 static void grant_immunity(object *disease) {
00585     object *immunity;
00586     object *walk;
00587 
00588     /* Don't give immunity to this disease if last_heal is set. */
00589     if (disease->last_heal)
00590         return;
00591 
00592     assert(disease->env != NULL);
00593 
00594     /*  first, search for an immunity of the same name */
00595     for (walk = disease->env->inv; walk; walk = walk->below) {
00596         if (walk->type == SIGN && !strcmp(disease->name, walk->name)) {
00597             walk->level = disease->level;
00598             return; /* just update the existing immunity. */
00599         }
00600     }
00601     immunity = create_archetype("immunity");
00602     immunity->name = add_string(disease->name);
00603     immunity->level = disease->level;
00604     immunity->move_block = 0;
00605     insert_ob_in_ob(immunity, disease->env);
00606     return;
00607 }
00608 
00615 void move_symptom(object *symptom) {
00616     object *victim = symptom->env;
00617     object *new_ob;
00618     int sp_reduce;
00619     tag_t tag = symptom->count;
00620 
00621     if (victim == NULL || victim->map == NULL) { /* outside a monster/player, die immediately */
00622         remove_ob(symptom);
00623         free_object(symptom);
00624         return;
00625     }
00626 
00627     if (symptom->stats.dam > 0)
00628         hit_player(victim, symptom->stats.dam, symptom, symptom->attacktype, 1);
00629     else
00630         hit_player(victim, MAX(1, -victim->stats.maxhp*symptom->stats.dam/100.0), symptom, symptom->attacktype, 1);
00631 
00632     /* In most cases, if the victim has been freed, the logic that
00633      * does that will also free the symptom, so check for that.
00634      */
00635     if (QUERY_FLAG(victim, FLAG_FREED)) {
00636         if (!was_destroyed(symptom, tag)) {
00637             remove_ob(symptom);
00638             free_object(symptom);
00639         }
00640         return;
00641     }
00642 
00643     if (symptom->stats.maxsp > 0)
00644         sp_reduce = symptom->stats.maxsp;
00645     else
00646         sp_reduce = MAX(1, victim->stats.maxsp*symptom->stats.maxsp/100.0);
00647     victim->stats.sp = MAX(0, victim->stats.sp-sp_reduce);
00648 
00649     /* create the symptom "other arch" object and drop it here
00650      * under every part of the monster
00651      * The victim may well have died.
00652      */
00653 
00654     if (symptom->other_arch) {
00655         object *tmp;
00656 
00657         tmp = victim;
00658         if (tmp->head != NULL)
00659             tmp = tmp->head;
00660         for (/*tmp initialized above */; tmp != NULL; tmp = tmp->more) {
00661             char name[MAX_BUF];
00662 
00663             new_ob = arch_to_object(symptom->other_arch);
00664             snprintf(name, sizeof(name), "%s's %s", victim->name, new_ob->name);
00665             FREE_AND_COPY(new_ob->name, name);
00666             if (new_ob->name_pl != NULL) {
00667                 snprintf(name, sizeof(name), "%s's %s", victim->name, new_ob->name_pl);
00668                 FREE_AND_COPY(new_ob->name_pl, name);
00669             }
00670             new_ob->x = tmp->x;
00671             new_ob->y = tmp->y;
00672             new_ob->map = victim->map;
00673             insert_ob_in_map(new_ob, victim->map, victim, 0);
00674         }
00675     }
00676     if (!symptom->msg) {
00677         LOG(llevError, "BUG: move_symptom(): symptom %d (%s) without message!\n", symptom->count, symptom->name);
00678         return;
00679     }
00680     draw_ext_info(NDI_UNIQUE|NDI_RED, 0, victim, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START,
00681                   symptom->msg, symptom->msg);
00682     return;
00683 }
00684 
00694 void check_physically_infect(object *victim, object *hitter) {
00695     object *walk;
00696 
00697     /* search for diseases, give every disease a chance to infect */
00698     for (walk = hitter->inv; walk != NULL; walk = walk->below)
00699         if (walk->type == DISEASE)
00700             infect_object(victim, walk, 0);
00701 }
00702 
00715 int cure_disease(object *sufferer, object *caster) {
00716     object *disease, *next;
00717     int casting_level;
00718     int cure = 0;
00719 
00720     if (caster)
00721         casting_level = caster->level;
00722     else
00723         casting_level = 1000;  /* if null caster, CURE all.  */
00724 
00725     for (disease = sufferer->inv; disease; disease = next) {
00726         next = disease->below;
00727 
00728         if (disease->type == DISEASE && !QUERY_FLAG(disease, FLAG_STARTEQUIP)) {
00729              /* attempt to cure this disease. God-given diseases are given by the god, so don't remove them */
00730             /* If caster lvel is higher than disease level, cure chance
00731              * is automatic.  If lower, then the chance is basically
00732              * 1 in level_diff - if there is a 5 level difference, chance
00733              * is 1 in 5.
00734              */
00735             if ((casting_level >= disease->level)
00736             || (!(random_roll(0, (disease->level-casting_level-1), caster, PREFER_LOW)))) {
00737                 remove_symptoms(disease);
00738                 remove_ob(disease);
00739                 cure = 1;
00740                 if (caster)
00741                     change_exp(caster, disease->stats.exp, caster->chosen_skill ? caster->chosen_skill->skill : NULL, 0);
00742                 free_object(disease);
00743             }
00744         }
00745     }
00746     if (cure) {
00747         /* Only draw these messages once */
00748         if (caster)
00749             draw_ext_info_format(NDI_UNIQUE, 0, caster, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL,
00750                                  "You cure a disease!", NULL);
00751 
00752         draw_ext_info(NDI_UNIQUE, 0, sufferer, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END,
00753                       "You no longer feel diseased.", NULL);
00754     }
00755     return cure;
00756 }