00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
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
00147
00148
00149
00150
00151
00152
00165 static int is_susceptible_to_disease(object *victim, object *disease) {
00166
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
00194
00195
00196
00197
00198 if (disease->env && QUERY_FLAG(disease->env, FLAG_WIZ))
00199 return 0;
00200
00201 if (disease->env == NULL) {
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
00211
00212 if (disease->stats.food > 0) {
00213 disease->stats.food--;
00214 if (disease->stats.food == 0) {
00215 remove_symptoms(disease);
00216 grant_immunity(disease);
00217 remove_ob(disease);
00218 free_object(disease);
00219 return 1;
00220 }
00221 }
00222 }
00223
00224 check_infection(disease);
00225
00226
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
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
00341 if (!QUERY_FLAG(victim, FLAG_MONSTER) && !(victim->type == PLAYER))
00342 return 0;
00343
00344
00345
00346
00347 if (!is_susceptible_to_disease(victim, disease))
00348 return 0;
00349
00350
00351
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
00358 if (!force && (random_roll(0, 126, victim, PREFER_HIGH) >= disease->stats.wc))
00359 return 0;
00360
00361
00362 if (victim->head)
00363 tmp = victim->head->inv;
00364 else
00365 tmp = victim->inv;
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375 for (; tmp; tmp = tmp->below) {
00376 if (tmp->type == SIGN && !strcmp(tmp->name, disease->name) && tmp->level >= disease->level)
00377 return 0;
00378 else if (tmp->type == DISEASE && !strcmp(tmp->name, disease->name))
00379 return 0;
00380 }
00381
00382
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;
00388
00389
00390
00391
00392
00393 if (get_owner(disease)) {
00394 set_owner(new_disease, disease->owner);
00395
00396
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 {
00404 if (disease->env && disease->env->type == PLAYER) {
00405 object *player = disease->env;
00406
00407 set_owner(new_disease, player);
00408
00409
00410
00411 }
00412 }
00413
00414 insert_ob_in_ob(new_disease, victim);
00415
00416
00417
00418 new_disease->move_block = 0;
00419 if (new_disease->owner && new_disease->owner->type == PLAYER) {
00420 char buf[128];
00421
00422
00423
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
00459
00460
00461
00462
00463 if (victim == NULL || victim == disease)
00464 return;
00465
00466
00467 if (QUERY_FLAG(victim, FLAG_WIZ))
00468 return;
00469
00470 symptom = find_symptom(disease);
00471 if (symptom == NULL) {
00472
00473 object *new_symptom;
00474
00475
00476 if (!is_susceptible_to_disease(victim, disease))
00477 return;
00478
00479
00480
00481 if (victim->head)
00482 tmp = victim->head->inv;
00483 else
00484 tmp = victim->inv;
00485
00486 for (; tmp; tmp = tmp->below) {
00487 if (tmp->type == SIGN)
00488 if (!strcmp(tmp->name, disease->name) && tmp->level >= disease->level)
00489 return;
00490 }
00491
00492 new_symptom = create_archetype(ARCH_SYMPTOM);
00493
00494
00495
00496
00497
00498 if (disease->stats.dam != 0) {
00499 int dam = disease->stats.dam;
00500
00501
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
00547
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
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
00589 if (disease->last_heal)
00590 return;
00591
00592 assert(disease->env != NULL);
00593
00594
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;
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) {
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
00633
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
00650
00651
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 != 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
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;
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
00730
00731
00732
00733
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
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 }