|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00036 #include <global.h> 00037 00038 static int is_susceptible_to_disease(object *victim, object *disease); 00039 static void remove_symptoms(object *disease); 00040 static object *find_symptom(object *disease); 00041 static void check_infection(object *disease); 00042 static void do_symptoms(object *disease); 00043 static void grant_immunity(object *disease); 00044 00050 static int is_susceptible_to_disease(object *victim, object *disease) 00051 { 00052 if (!IS_LIVE(victim)) 00053 { 00054 return 0; 00055 } 00056 00057 if (strstr(disease->race, "*") && !QUERY_FLAG(victim, FLAG_UNDEAD)) 00058 { 00059 return 1; 00060 } 00061 00062 if (strstr(disease->race, "undead") && QUERY_FLAG(victim, FLAG_UNDEAD)) 00063 { 00064 return 1; 00065 } 00066 00067 if ((victim->race && strstr(disease->race, victim->race)) || strstr(disease->race, victim->name)) 00068 { 00069 return 1; 00070 } 00071 00072 return 0; 00073 } 00074 00079 int move_disease(object *disease) 00080 { 00081 /* first task is to determine if the disease is inside or outside of someone. 00082 * If outside, we decrement 'value' until we're gone. */ 00083 00084 /* we're outside of someone */ 00085 if (disease->env == NULL) 00086 { 00087 disease->value--; 00088 00089 if (disease->value == 0) 00090 { 00091 /* drop inv since disease may carry secondary infections */ 00092 destruct_ob(disease); 00093 return 1; 00094 } 00095 } 00096 else 00097 { 00098 /* if we're inside a person, have the disease run its course. 00099 * negative foods denote "perpetual" diseases. */ 00100 if (disease->stats.food > 0 && is_susceptible_to_disease(disease->env, disease)) 00101 { 00102 disease->stats.food--; 00103 00104 if (disease->stats.food == 0) 00105 { 00106 /* remove the symptoms of this disease */ 00107 remove_symptoms(disease); 00108 grant_immunity(disease); 00109 /* drop inv since disease may carry secondary infections */ 00110 destruct_ob(disease); 00111 return 1; 00112 } 00113 } 00114 } 00115 00116 /* check to see if we infect others */ 00117 check_infection(disease); 00118 00119 /* impose or modify the symptoms of the disease */ 00120 if (disease->env) 00121 { 00122 do_symptoms(disease); 00123 } 00124 00125 return 0; 00126 } 00127 00131 static void remove_symptoms(object *disease) 00132 { 00133 object *symptom = find_symptom(disease); 00134 00135 if (symptom != NULL) 00136 { 00137 object *victim = symptom->env; 00138 destruct_ob(symptom); 00139 00140 if (victim) 00141 { 00142 fix_player(victim); 00143 } 00144 } 00145 } 00146 00151 static object *find_symptom(object *disease) 00152 { 00153 object *walk; 00154 00155 /* check the inventory for symptoms */ 00156 for (walk = disease->env->inv; walk; walk = walk->below) 00157 { 00158 if (!strcmp(walk->name, disease->name) && walk->type == SYMPTOM) 00159 { 00160 return walk; 00161 } 00162 } 00163 00164 return NULL; 00165 } 00166 00170 static void check_infection(object *disease) 00171 { 00172 int x, y, i, j, range, xt, yt; 00173 mapstruct *map, *m; 00174 object *tmp; 00175 rv_vector rv; 00176 00177 range = abs(disease->magic); 00178 00179 if (disease->env) 00180 { 00181 x = disease->env->x; 00182 y = disease->env->y; 00183 map = disease->env->map; 00184 } 00185 else 00186 { 00187 x = disease->x; 00188 y = disease->y; 00189 map = disease->map; 00190 } 00191 00192 if (map == NULL) 00193 { 00194 return; 00195 } 00196 00197 for (i = -range; i <= range; i++) 00198 { 00199 for (j = -range; j <= range; j++) 00200 { 00201 xt = x + i; 00202 yt = y + j; 00203 00204 if (!(m = get_map_from_coord(map, &xt, &yt))) 00205 { 00206 continue; 00207 } 00208 00209 if (!(GET_MAP_FLAGS(m, xt, yt) & (P_IS_ALIVE | P_IS_PLAYER))) 00210 { 00211 continue; 00212 } 00213 00214 for (tmp = GET_MAP_OB(m, xt, yt); tmp; tmp = tmp->above) 00215 { 00216 if (!QUERY_FLAG(tmp, FLAG_MONSTER) && tmp->type != PLAYER) 00217 { 00218 continue; 00219 } 00220 00221 if (!get_rangevector(disease->env ? disease->env : disease, tmp, &rv, 0) || !obj_in_line_of_sight(tmp, &rv)) 00222 { 00223 continue; 00224 } 00225 00226 infect_object(tmp, disease, 0); 00227 } 00228 } 00229 } 00230 } 00231 00243 int infect_object(object *victim, object *disease, int force) 00244 { 00245 object *tmp, *new_disease, *owner; 00246 00247 /* only use the head */ 00248 if (victim->head) 00249 { 00250 victim = victim->head; 00251 } 00252 00253 /* don't infect inanimate objects */ 00254 if (!QUERY_FLAG(victim, FLAG_MONSTER) && victim->type != PLAYER) 00255 { 00256 return 0; 00257 } 00258 00259 if ((owner = get_owner(disease))) 00260 { 00261 if (is_friend_of(owner, victim)) 00262 { 00263 return 0; 00264 } 00265 } 00266 00267 /* check and see if victim can catch disease: diseases are specific */ 00268 if (!is_susceptible_to_disease(victim, disease)) 00269 { 00270 return 0; 00271 } 00272 00273 /* roll the dice on infection before doing the inventory check! */ 00274 if (!force && (rndm(0, 126) >= disease->stats.wc)) 00275 { 00276 return 0; 00277 } 00278 00279 for (tmp = victim->inv; tmp; tmp = tmp->below) 00280 { 00281 /* possibly an immunity, or diseased */ 00282 if (tmp->type == SIGN || tmp->type == DISEASE) 00283 { 00284 if (!strcmp(tmp->name, disease->name) && tmp->level >= disease->level) 00285 { 00286 return 0; 00287 } 00288 } 00289 } 00290 00291 /* If we've gotten this far, go ahead and infect the victim. */ 00292 new_disease = get_object(); 00293 copy_object(disease, new_disease, 0); 00294 new_disease->stats.food = disease->stats.maxgrace; 00295 new_disease->value = disease->stats.maxhp; 00296 /* self-limiting factor */ 00297 new_disease->stats.wc -= disease->last_grace; 00298 00299 /* Unfortunately, set_owner does the wrong thing to the skills pointers 00300 * resulting in exp going into the owners *current* chosen skill. */ 00301 if (get_owner(disease)) 00302 { 00303 set_owner(new_disease, disease->owner); 00304 new_disease->chosen_skill = disease->chosen_skill; 00305 new_disease->exp_obj = disease->exp_obj; 00306 } 00307 /* for diseases which are passed by hitting, set owner and praying skill */ 00308 else 00309 { 00310 if (disease->env && disease->env->type == PLAYER) 00311 { 00312 object *pl = disease->env; 00313 00314 /* hm, we should for hit use the weapon? or the skill attached to this 00315 * specific disease? hmmm */ 00316 new_disease->chosen_skill = find_skill(pl, SK_PRAYING); 00317 00318 if (new_disease->chosen_skill) 00319 { 00320 set_owner(new_disease, pl); 00321 new_disease->exp_obj = new_disease->chosen_skill->exp_obj; 00322 } 00323 } 00324 } 00325 00326 insert_ob_in_ob(new_disease, victim); 00327 CLEAR_FLAG(new_disease, FLAG_NO_PASS); 00328 00329 if (new_disease->owner && new_disease->owner->type == PLAYER) 00330 { 00331 char buf[128]; 00332 00333 /* if the disease has a title, it has a special infection message */ 00334 if (new_disease->title) 00335 { 00336 snprintf(buf, sizeof(buf), "%s %s!!", disease->title, victim->name); 00337 } 00338 else 00339 { 00340 snprintf(buf, sizeof(buf), "You infect %s with your disease, %s!", victim->name, new_disease->name); 00341 } 00342 00343 if (victim->type == PLAYER) 00344 { 00345 new_draw_info(0, COLOR_RED, new_disease->owner, buf); 00346 } 00347 else 00348 { 00349 new_draw_info(0, COLOR_WHITE, new_disease->owner, buf); 00350 } 00351 } 00352 00353 if (victim->type == PLAYER) 00354 { 00355 new_draw_info(0, COLOR_RED, victim, "You suddenly feel ill."); 00356 } 00357 00358 return 1; 00359 } 00360 00366 static void do_symptoms(object *disease) 00367 { 00368 object *symptom, *victim = disease->env, *tmp; 00369 00370 /* This is a quick hack - for whatever reason, disease->env will point 00371 * back to disease, causing endless loops. Why this happens really needs 00372 * to be found, but this should at least prevent the infinite loops. */ 00373 00374 /* no-one to inflict symptoms on. */ 00375 if (victim == NULL || victim == disease) 00376 { 00377 return; 00378 } 00379 00380 symptom = find_symptom(disease); 00381 00382 /* no symptom? need to generate one! */ 00383 if (symptom == NULL) 00384 { 00385 object *new_symptom; 00386 int i; 00387 00388 /* first check and see if the carrier of the disease 00389 * is immune. If so, no symptoms! */ 00390 if (!is_susceptible_to_disease(victim, disease)) 00391 { 00392 return; 00393 } 00394 00395 /* check for an actual immunity */ 00396 /* do an immunity check */ 00397 /* hm, disease should be NEVER in a tail of a multi part object */ 00398 if (victim->head) 00399 { 00400 tmp = victim->head->inv; 00401 } 00402 else 00403 { 00404 tmp = victim->inv; 00405 } 00406 00407 /* tmp initialized in if, above */ 00408 for (; tmp; tmp = tmp->below) 00409 { 00410 /* possibly an immunity, or diseased */ 00411 if (tmp->type == SIGN) 00412 { 00413 if (!strcmp(tmp->name, disease->name) && tmp->level >= disease->level) 00414 { 00415 return; 00416 } 00417 } 00418 } 00419 00420 new_symptom = get_archetype("symptom"); 00421 00422 /* Something special done with dam. We want diseases to be more 00423 * random in what they'll kill, so we'll make the damage they 00424 * do random, note, this has a weird effect with progressive diseases. */ 00425 if (disease->stats.dam != 0) 00426 { 00427 int dam = disease->stats.dam; 00428 00429 /* reduce the damage, on average, 50%, and making things random. */ 00430 dam = rndm(1, FABS(dam)); 00431 00432 if (disease->stats.dam < 0) 00433 { 00434 dam = -dam; 00435 } 00436 00437 new_symptom->stats.dam = dam; 00438 } 00439 00440 new_symptom->stats.maxsp = disease->stats.maxsp; 00441 new_symptom->stats.food = new_symptom->stats.maxgrace; 00442 00443 FREE_AND_COPY_HASH(new_symptom->name, disease->name); 00444 new_symptom->level = disease->level; 00445 new_symptom->speed = disease->speed; 00446 new_symptom->value = 0; 00447 new_symptom->stats.Str = disease->stats.Str; 00448 new_symptom->stats.Dex = disease->stats.Dex; 00449 new_symptom->stats.Con = disease->stats.Con; 00450 new_symptom->stats.Wis = disease->stats.Wis; 00451 new_symptom->stats.Int = disease->stats.Int; 00452 new_symptom->stats.Pow = disease->stats.Pow; 00453 new_symptom->stats.Cha = disease->stats.Cha; 00454 new_symptom->stats.sp = disease->stats.sp; 00455 new_symptom->stats.food = disease->last_eat; 00456 new_symptom->stats.maxsp = disease->stats.maxsp; 00457 new_symptom->last_sp = disease->last_sp; 00458 new_symptom->stats.exp = 0; 00459 new_symptom->stats.hp = disease->stats.hp; 00460 FREE_AND_COPY_HASH(new_symptom->msg, disease->msg); 00461 new_symptom->other_arch = disease->other_arch; 00462 00463 for (i = 0; i < NROFATTACKS; i++) 00464 { 00465 if (disease->attack[i]) 00466 { 00467 new_symptom->attack[i] = disease->attack[i]; 00468 } 00469 } 00470 00471 set_owner(new_symptom, disease->owner); 00472 00473 /* Unfortunately, set_owner does the wrong thing to the skills pointers 00474 * resulting in exp going into the owners *current* chosen skill. */ 00475 new_symptom->chosen_skill = disease->chosen_skill; 00476 new_symptom->exp_obj = disease->exp_obj; 00477 00478 CLEAR_FLAG(new_symptom, FLAG_NO_PASS); 00479 insert_ob_in_ob(new_symptom, victim); 00480 return; 00481 } 00482 00483 /* now deal with progressing diseases: we increase the debility 00484 * caused by the symptoms. */ 00485 if (disease->stats.ac != 0) 00486 { 00487 float scale; 00488 00489 symptom->value += disease->stats.ac; 00490 scale = (float) 1.0 + (float) symptom->value / (float) 100.0; 00491 00492 /* now rescale all the debilities */ 00493 symptom->stats.Str = (int) (scale * disease->stats.Str); 00494 symptom->stats.Dex = (int) (scale * disease->stats.Dex); 00495 symptom->stats.Con = (int) (scale * disease->stats.Con); 00496 symptom->stats.Wis = (int) (scale * disease->stats.Wis); 00497 symptom->stats.Int = (int) (scale * disease->stats.Int); 00498 symptom->stats.Pow = (int) (scale * disease->stats.Pow); 00499 symptom->stats.Cha = (int) (scale * disease->stats.Cha); 00500 symptom->stats.dam = (int) (scale * disease->stats.dam); 00501 symptom->stats.sp = (int) (scale * disease->stats.sp); 00502 symptom->stats.food = (int) (scale * disease->last_eat); 00503 symptom->stats.maxsp = (int) (scale * disease->stats.maxsp); 00504 symptom->last_sp = (int) (scale * disease->last_sp); 00505 symptom->stats.exp = 0; 00506 symptom->stats.hp = (int) (scale * disease->stats.hp); 00507 FREE_AND_COPY_HASH(symptom->msg, disease->msg); 00508 symptom->other_arch = disease->other_arch; 00509 } 00510 00511 SET_FLAG(symptom, FLAG_APPLIED); 00512 fix_player(victim); 00513 } 00514 00518 static void grant_immunity(object *disease) 00519 { 00520 object *immunity, *walk; 00521 00522 /* Don't give immunity to this disease if last_heal is set. */ 00523 if (disease->last_heal) 00524 { 00525 return; 00526 } 00527 00528 /* first, search for an immunity of the same name */ 00529 for (walk = disease->env->inv; walk; walk = walk->below) 00530 { 00531 if (walk->type == SIGN && !strcmp(disease->name, walk->name)) 00532 { 00533 walk->level = disease->level; 00534 /* just update the existing immunity. */ 00535 return; 00536 } 00537 } 00538 00539 immunity = get_archetype("immunity"); 00540 FREE_AND_COPY_HASH(immunity->name, disease->name); 00541 immunity->level = disease->level; 00542 CLEAR_FLAG(immunity, FLAG_NO_PASS); 00543 insert_ob_in_ob(immunity, disease->env); 00544 } 00545 00549 void move_symptom(object *symptom) 00550 { 00551 object *victim = symptom->env; 00552 object *new_ob; 00553 int sp_reduce; 00554 00555 /* outside a monster/player, die immediately */ 00556 if (victim == NULL || victim->map == NULL) 00557 { 00558 remove_ob(symptom); 00559 check_walk_off(symptom, NULL, MOVE_APPLY_VANISHED); 00560 return; 00561 } 00562 00563 if (symptom->stats.dam > 0) 00564 { 00565 hit_player(victim, symptom->stats.dam, symptom, AT_INTERNAL); 00566 } 00567 else 00568 { 00569 hit_player(victim, (int) MAX((float) 1, (float) - victim->stats.maxhp * (float) symptom->stats.dam / (float) 100.0), symptom, AT_INTERNAL); 00570 } 00571 00572 if (symptom->stats.maxsp > 0) 00573 { 00574 sp_reduce = symptom->stats.maxsp; 00575 } 00576 else 00577 { 00578 sp_reduce = (int) MAX((float) 1, (float) victim->stats.maxsp * (float) symptom->stats.maxsp / (float) 100.0); 00579 } 00580 00581 victim->stats.sp = MAX(0, victim->stats.sp - sp_reduce); 00582 00583 /* create the symptom "other arch" object and drop it here 00584 * under every part of the monster */ 00585 00586 /* The victim may well have died. */ 00587 if (victim->map == NULL) 00588 { 00589 return; 00590 } 00591 00592 if (symptom->other_arch) 00593 { 00594 object *tmp = victim; 00595 00596 if (tmp->head != NULL) 00597 { 00598 tmp = tmp->head; 00599 } 00600 00601 /* tmp initialized above */ 00602 for (; tmp != NULL; tmp = tmp->more) 00603 { 00604 new_ob = arch_to_object(symptom->other_arch); 00605 new_ob->x = tmp->x; 00606 new_ob->y = tmp->y; 00607 new_ob->map = victim->map; 00608 insert_ob_in_map(new_ob, victim->map, victim, INS_NO_MERGE | INS_NO_WALK_ON); 00609 } 00610 } 00611 00612 if (victim->type == PLAYER) 00613 { 00614 new_draw_info(0, COLOR_RED, victim, symptom->msg); 00615 } 00616 } 00617 00624 void check_physically_infect(object *victim, object *hitter) 00625 { 00626 object *walk; 00627 00628 /* search for diseases, give every disease a chance to infect */ 00629 for (walk = hitter->inv; walk != NULL; walk = walk->below) 00630 { 00631 if (walk->type == DISEASE) 00632 { 00633 infect_object(victim, walk, 0); 00634 } 00635 } 00636 } 00637 00645 int cure_disease(object *sufferer, object *caster) 00646 { 00647 object *disease, *next; 00648 int casting_level, is_disease = 0; 00649 00650 if (caster) 00651 { 00652 casting_level = caster->level; 00653 } 00654 else 00655 { 00656 caster = sufferer; 00657 /* if null caster, CURE all. */ 00658 casting_level = 1000; 00659 } 00660 00661 if (caster != sufferer && sufferer->type == PLAYER) 00662 { 00663 new_draw_info_format(0, COLOR_WHITE, sufferer, "%s casts cure disease on you!", caster->name ? caster->name : "someone"); 00664 } 00665 00666 for (disease = sufferer->inv; disease; disease = next) 00667 { 00668 next = disease->below; 00669 00670 /* attempt to cure this disease */ 00671 /* If caster level is higher than disease level, cure chance 00672 * is automatic. If lower, then the chance is basically 00673 * 1 in level_diff - if there is a 5 level difference, chance 00674 * is 1 in 5. */ 00675 if (disease->type == DISEASE) 00676 { 00677 is_disease = 1; 00678 00679 if ((casting_level >= disease->level) || (!(rndm(0, (disease->level - casting_level - 1))))) 00680 { 00681 if (sufferer->type == PLAYER) 00682 { 00683 new_draw_info_format(0, COLOR_WHITE, sufferer, "You are healed from disease %s.", disease->name); 00684 } 00685 00686 if (sufferer != caster && caster->type == PLAYER) 00687 { 00688 new_draw_info_format(0, COLOR_WHITE, caster, "You heal %s from disease %s.", sufferer->name, disease->name); 00689 } 00690 00691 remove_symptoms(disease); 00692 remove_ob(disease); 00693 00694 /* we assume the caster has the right casting skill applied */ 00695 if (caster && !caster->type == PLAYER) 00696 { 00697 add_exp(caster, disease->stats.exp, caster->chosen_skill->stats.sp, 0); 00698 } 00699 } 00700 else 00701 { 00702 if (sufferer->type == PLAYER) 00703 { 00704 new_draw_info_format(0, COLOR_WHITE, sufferer, "The disease %s resists the cure prayer!", disease->name); 00705 } 00706 00707 if (sufferer != caster && caster->type == PLAYER) 00708 { 00709 new_draw_info_format(0, COLOR_WHITE, caster, "The disease %s resists the cure prayer!", disease->name); 00710 } 00711 } 00712 } 00713 } 00714 00715 if (!is_disease) 00716 { 00717 if (sufferer->type == PLAYER) 00718 { 00719 new_draw_info(0, COLOR_WHITE, sufferer, "You are not diseased!"); 00720 } 00721 00722 if (sufferer != caster && caster->type == PLAYER) 00723 { 00724 new_draw_info_format(0, COLOR_WHITE, caster, "%s is not diseased!", sufferer->name ? sufferer->name : "someone"); 00725 } 00726 } 00727 00728 return 1; 00729 } 00730 00736 int reduce_symptoms(object *sufferer, int reduction) 00737 { 00738 object *walk; 00739 int success = 0; 00740 00741 for (walk = sufferer->inv; walk; walk = walk->below) 00742 { 00743 if (walk->type == SYMPTOM) 00744 { 00745 if (walk->value > 0) 00746 { 00747 success = 1; 00748 walk->value = MAX(0, walk->value - 2 * reduction); 00749 /* give the disease time to modify this symptom, 00750 * and reduce its severity. */ 00751 walk->speed_left = 0; 00752 } 00753 } 00754 } 00755 00756 if (success) 00757 { 00758 new_draw_info(0, COLOR_WHITE, sufferer, "Your illness seems less severe."); 00759 } 00760 00761 return success; 00762 }
1.7.4