Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_living_c = 00003 * "$Id: living.c 11190 2009-01-19 05:07:11Z lalo $"; 00004 */ 00005 00006 /* 00007 CrossFire, A Multiplayer game for X-windows 00008 00009 Copyright (C) 2002-2006 Mark Wedel & Crossfire Development Team 00010 Copyright (C) 1992 Frank Tore Johansen 00011 00012 This program is free software; you can redistribute it and/or modify 00013 it under the terms of the GNU General Public License as published by 00014 the Free Software Foundation; either version 2 of the License, or 00015 (at your option) any later version. 00016 00017 This program is distributed in the hope that it will be useful, 00018 but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 GNU General Public License for more details. 00021 00022 You should have received a copy of the GNU General Public License 00023 along with this program; if not, write to the Free Software 00024 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00025 00026 The authors can be reached via e-mail at crossfire-devel@real-time.com 00027 */ 00028 00037 #include <stdlib.h> 00038 #include <global.h> 00039 #include <sproto.h> 00040 00046 #define ADD_EXP(exptotal, exp) {exptotal += exp; if (exptotal > MAX_EXPERIENCE) exptotal = MAX_EXPERIENCE; } 00047 00051 static const int con_bonus[MAX_STAT + 1]={ 00052 -6,-5,-4,-3,-2,-1,-1,0,0,0,0,1,2,3,4,5,6,7,8,9,10,12,14,16,18,20, 00053 22,25,30,40,50 00054 }; 00055 00062 static const int sp_bonus[MAX_STAT + 1]={ 00063 -10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,12,15,20,25, 00064 30,40,50,70,100 00065 }; 00066 00070 static const int grace_bonus[MAX_STAT +1] = { 00071 -10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,12,15,20,25, 00072 30,40,50,70,100 00073 }; 00074 00094 const float cha_bonus[MAX_STAT + 1]={ 00095 10.0, 10.0, 9.0, 8.0, 7.0, 6.0, /*<-5*/ 00096 5.0, 4.5, 4.0, 3.5, 3.0, /*<-10*/ 00097 2.9, 2.8, 2.7, 2.6, 2.5, /*<-15*/ 00098 2.4, 2.3, 2.2, 2.1, 2.0, /*<-20*/ 00099 1.95, 1.90, 1.85, 1.80, 1.75, /*25 */ 00100 1.70, 1.65, 1.60, 1.55, 1.50 /*30 */ 00101 }; 00102 00104 const int dex_bonus[MAX_STAT + 1]={ 00105 -4,-3,-2,-2,-1,-1,-1,0,0,0,0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,7 00106 }; 00107 00109 const float speed_bonus[MAX_STAT + 1]={ 00110 -0.4, -0.4, -0.3, -0.3, -0.2, -0.2, -0.2, -0.1, -0.1, -0.1, -0.05, 0, 0, 0, 00111 0.05, 0.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.4, 00112 1.6, 1.8, 2.0, 2.5, 3.0 00113 }; 00114 00119 const int dam_bonus[MAX_STAT + 1]={ 00120 -2,-2,-2,-1,-1,-1,0,0,0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,5,5,6,6,7,8,10,15 00121 }; 00122 00124 const int thaco_bonus[MAX_STAT + 1]={ 00125 -2,-2,-1,-1,0,0,0,0,0,0,0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,5,5,6,7,8,10 00126 }; 00127 00128 /* Max you can carry before you start getting extra speed penalties */ 00129 const int max_carry[MAX_STAT + 1]={ 00130 2,4,7,11,16,22,29,37,46,56,67,79,92,106,121,137,154,172,191,211,232,254,277, 00131 301,326,352,400,450,500,600,1000 00132 }; 00133 00143 const uint32 weight_limit[MAX_STAT+ 1] = { 00144 200000, /* 0 */ 00145 250000,300000,350000,400000,500000, /* 5*/ 00146 600000,700000,800000,900000,1000000, /* 10 */ 00147 1100000,1200000,1300000,1400000,1500000, /* 15 */ 00148 1650000,1800000,1950000,2100000,2250000, /* 20 */ 00149 2400000,2550000,2700000,2850000,3000000, /* 25 */ 00150 3250000,3500000,3750000,4000000,4500000 /*30 */ 00151 }; 00152 00154 const int learn_spell[MAX_STAT + 1]={ 00155 0,0,0,1,2,4,8,12,16,25,36,45,55,65,70,75,80,85,90,95,100,100,100,100,100, 00156 100,100,100,100,100,100 00157 }; 00158 00160 const int cleric_chance[MAX_STAT + 1]={ 00161 100,100,100,100,90,80,70,60,50,40,35,30,25,20,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0 00162 }; 00163 00165 const int turn_bonus[MAX_STAT + 1]={ 00166 -1,-1,-1,-1,-1,-1,-1,-1,0,0,0,1,1,1,2,2,2,3,3,3,4,4,5,5,6,7,8,9,10,12,15 00167 }; 00168 00170 const int fear_bonus[MAX_STAT + 1]={ 00171 3,3,3,3,2,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 00172 }; 00173 00178 #define MAX_EXPERIENCE levels[settings.max_level] 00179 00195 #define MAX_EXP_IN_OBJ levels[settings.max_level]/(MAX_EXP_CAT - 1) 00196 00197 extern sint64 *levels; 00198 00199 #define MAX_SAVE_LEVEL 110 00200 00209 static const int savethrow[MAX_SAVE_LEVEL+1]={ 00210 18, 00211 18,17,16,15,14,14,13,13,12,12,12,11,11,11,11,10,10,10,10, 9, 00212 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 00213 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 00214 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 00215 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 00216 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 00217 }; 00218 00220 const char *const attacks[NROFATTACKS] = { 00221 "physical", "magical", "fire", "electricity", "cold", "confusion", 00222 "acid", "drain", "weaponmagic", "ghosthit", "poison", "slow", 00223 "paralyze", "turn undead", "fear", "cancellation", "depletion", "death", 00224 "chaos","counterspell","god power","holy power","blinding", "", 00225 "life stealing" 00226 }; 00227 00229 static const char *const drain_msg[NUM_STATS] = { 00230 "Oh no! You are weakened!", 00231 "You're feeling clumsy!", 00232 "You feel less healthy", 00233 "You suddenly begin to lose your memory!", 00234 "Your face gets distorted!", 00235 "Watch out, your mind is going!", 00236 "Your spirit feels drained!" 00237 }; 00238 00240 const char *const restore_msg[NUM_STATS] = { 00241 "You feel your strength return.", 00242 "You feel your agility return.", 00243 "You feel your health return.", 00244 "You feel your wisdom return.", 00245 "You feel your charisma return.", 00246 "You feel your memory return.", 00247 "You feel your spirits return." 00248 }; 00249 00251 const char *const gain_msg[NUM_STATS] = { 00252 "You feel stronger.", 00253 "You feel more agile.", 00254 "You feel healthy.", 00255 "You feel wiser.", 00256 "You seem to look better.", 00257 "You feel smarter.", 00258 "You feel more potent." 00259 }; 00260 00262 const char *const lose_msg[NUM_STATS] = { 00263 "You feel weaker!", 00264 "You feel clumsy!", 00265 "You feel less healthy!", 00266 "You lose some of your memory!", 00267 "You look ugly!", 00268 "You feel stupid!", 00269 "You feel less potent!" 00270 }; 00271 00273 const char *const statname[NUM_STATS] = { 00274 "strength", "dexterity", "constitution", "wisdom", "charisma", "intelligence","power" 00275 }; 00276 00278 const char *const short_stat_name[NUM_STATS] = { 00279 "Str", "Dex", "Con", "Wis", "Cha", "Int","Pow" 00280 }; 00281 00296 void set_attr_value(living *stats,int attr,sint8 value) { 00297 switch (attr) { 00298 case STR: 00299 stats->Str=value; 00300 break; 00301 case DEX: 00302 stats->Dex=value; 00303 break; 00304 case CON: 00305 stats->Con=value; 00306 break; 00307 case WIS: 00308 stats->Wis=value; 00309 break; 00310 case POW: 00311 stats->Pow=value; 00312 break; 00313 case CHA: 00314 stats->Cha=value; 00315 break; 00316 case INT: 00317 stats->Int=value; 00318 break; 00319 } 00320 } 00321 00336 void change_attr_value(living *stats,int attr,sint8 value) { 00337 if (value==0) return; 00338 switch (attr) { 00339 case STR: 00340 stats->Str+=value; 00341 break; 00342 case DEX: 00343 stats->Dex+=value; 00344 break; 00345 case CON: 00346 stats->Con+=value; 00347 break; 00348 case WIS: 00349 stats->Wis+=value; 00350 break; 00351 case POW: 00352 stats->Pow+=value; 00353 break; 00354 case CHA: 00355 stats->Cha+=value; 00356 break; 00357 case INT: 00358 stats->Int+=value; 00359 break; 00360 default: 00361 LOG(llevError,"Invalid attribute in change_attr_value: %d\n", attr); 00362 } 00363 } 00364 00377 sint8 get_attr_value(const living *stats,int attr) { 00378 switch (attr) { 00379 case STR: 00380 return(stats->Str); 00381 case DEX: 00382 return(stats->Dex); 00383 case CON: 00384 return(stats->Con); 00385 case WIS: 00386 return(stats->Wis); 00387 case CHA: 00388 return(stats->Cha); 00389 case INT: 00390 return(stats->Int); 00391 case POW: 00392 return(stats->Pow); 00393 } 00394 return 0; 00395 } 00396 00404 void check_stat_bounds(living *stats) { 00405 int i,v; 00406 for (i=0;i<NUM_STATS;i++) 00407 if ((v=get_attr_value(stats,i))>MAX_STAT) 00408 set_attr_value(stats,i,MAX_STAT); 00409 else if (v<MIN_STAT) 00410 set_attr_value(stats,i,MIN_STAT); 00411 } 00412 00418 #define DIFF_MSG(flag, subtype1, subtype2, msg1, msg2) \ 00419 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, (flag>0)?subtype1:subtype2, (flag>0)?msg1:msg2, NULL); 00420 00443 int change_abil(object *op, object *tmp) { 00444 int flag=QUERY_FLAG(tmp,FLAG_APPLIED)?1:-1,i,j,success=0; 00445 object refop; 00446 int potion_max=0; 00447 00448 /* remember what object was like before it was changed. note that 00449 * refop is a local copy of op only to be used for detecting changes 00450 * found by fix_object. refop is not a real object 00451 */ 00452 memcpy(&refop, op, sizeof(object)); 00453 00454 if (op->type==PLAYER) { 00455 if (tmp->type==POTION) { 00456 potion_max=1; 00457 for (j=0;j<NUM_STATS;j++) { 00458 int nstat, ostat; 00459 00460 ostat = get_attr_value(&(op->contr->orig_stats),j); 00461 i = get_attr_value(&(tmp->stats),j); 00462 00463 /* nstat is what the stat will be after use of the potion */ 00464 nstat = flag*i + ostat; 00465 00466 /* Do some bounds checking. While I don't think any 00467 * potions do so right now, there is the potential for potions 00468 * that adjust that stat by more than one point, so we need 00469 * to allow for that. 00470 */ 00471 if (nstat < 1 && i*flag < 0) nstat = 1; 00472 else if (nstat > 20 + get_attr_value(&(op->arch->clone.stats),j)) { 00473 nstat = 20 + get_attr_value(&(op->arch->clone.stats),j); 00474 } 00475 if (nstat != ostat) { 00476 set_attr_value(&(op->contr->orig_stats), j, nstat); 00477 potion_max=0; 00478 } else if (i) { 00479 /* potion is useless - player has already hit the natural maximum */ 00480 potion_max = 1; 00481 } 00482 } 00483 /* This section of code ups the characters normal stats also. I am not 00484 * sure if this is strictly necessary, being that fix_object probably 00485 * recalculates this anyway. 00486 */ 00487 for (j=0;j<NUM_STATS;j++) 00488 change_attr_value(&(op->stats),j,flag*get_attr_value(&(tmp->stats),j)); 00489 check_stat_bounds(&(op->stats)); 00490 } /* end of potion handling code */ 00491 } 00492 00493 /* reset attributes that fix_object doesn't reset since it doesn't search 00494 * everything to set 00495 */ 00496 if (flag == -1) { 00497 op->attacktype&=~tmp->attacktype; 00498 op->path_attuned&=~tmp->path_attuned; 00499 op->path_repelled&=~tmp->path_repelled; 00500 op->path_denied&=~tmp->path_denied; 00501 /* Presuming here that creatures only have move_type, 00502 * and not the other move_ fields. 00503 */ 00504 op->move_type &= ~tmp->move_type; 00505 } 00506 00507 /* call fix_object since op object could have whatever attribute due 00508 * to multiple items. if fix_object always has to be called after 00509 * change_ability then might as well call it from here 00510 */ 00511 fix_object(op); 00512 00513 /* Fix player won't add the bows ability to the player, so don't 00514 * print out message if this is a bow. 00515 */ 00516 if (tmp->attacktype & AT_CONFUSION && tmp->type != BOW) { 00517 success=1; 00518 DIFF_MSG(flag, MSG_TYPE_ATTRIBUTE_ATTACKTYPE_GAIN, MSG_TYPE_ATTRIBUTE_ATTACKTYPE_LOSS, 00519 "Your hands begin to glow red.", 00520 "Your hands stop glowing red."); 00521 } 00522 if (QUERY_FLAG(op,FLAG_LIFESAVE) != QUERY_FLAG(&refop,FLAG_LIFESAVE)) { 00523 success=1; 00524 DIFF_MSG(flag, MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN, MSG_TYPE_ATTRIBUTE_PROTECTION_LOSS, 00525 "You feel very protected.", 00526 "You don't feel protected anymore."); 00527 } 00528 if (QUERY_FLAG(op,FLAG_REFL_MISSILE) != QUERY_FLAG(&refop,FLAG_REFL_MISSILE)) { 00529 success=1; 00530 DIFF_MSG(flag, MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN, MSG_TYPE_ATTRIBUTE_PROTECTION_LOSS, 00531 "A magic force shimmers around you.", 00532 "The magic force fades away."); 00533 } 00534 if (QUERY_FLAG(op,FLAG_REFL_SPELL) != QUERY_FLAG(&refop,FLAG_REFL_SPELL)) { 00535 success=1; 00536 DIFF_MSG(flag, MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN, MSG_TYPE_ATTRIBUTE_PROTECTION_LOSS, 00537 "You feel more safe now, somehow.", 00538 "Suddenly you feel less safe, somehow."); 00539 } 00540 /* movement type has changed. We don't care about cases where 00541 * user has multiple items giving the same type appled like we 00542 * used to - that is more work than what we gain, plus messages 00543 * can be misleading (a little higher could be miscontrued from 00544 * from fly high) 00545 */ 00546 if (tmp->move_type && op->move_type != refop.move_type) { 00547 success=1; 00548 00549 /* MOVE_FLY_HIGH trumps MOVE_FLY_LOW - changing your move_fly_low 00550 * status doesn't make a difference if you are flying high 00551 */ 00552 if (tmp->move_type & MOVE_FLY_LOW && !(op->move_type & MOVE_FLY_HIGH)) { 00553 DIFF_MSG(flag, MSG_TYPE_ATTRIBUTE_MOVE, MSG_TYPE_ATTRIBUTE_MOVE, 00554 "You start to float in the air!.", 00555 "You float down to the ground."); 00556 } 00557 00558 if (tmp->move_type & MOVE_FLY_HIGH) { 00559 /* double conditional - second case covers if you have move_fly_low - 00560 * in that case, you don't actually land 00561 */ 00562 DIFF_MSG(flag, MSG_TYPE_ATTRIBUTE_MOVE, MSG_TYPE_ATTRIBUTE_MOVE, 00563 "You soar into the air air!.", 00564 (op->move_type&MOVE_FLY_LOW ? "You fly lower in the air": 00565 "You float down to the ground.")); 00566 } 00567 if (tmp->move_type & MOVE_SWIM) 00568 DIFF_MSG(flag, MSG_TYPE_ATTRIBUTE_MOVE, MSG_TYPE_ATTRIBUTE_MOVE, 00569 "You feel ready for a swim", 00570 "You no longer feel like swimming"); 00571 00572 /* Changing move status may mean you are affected by things you weren't before */ 00573 check_move_on(op, op); 00574 } 00575 00576 /* becoming UNDEAD... a special treatment for this flag. Only those not 00577 * originally undead may change their status 00578 */ 00579 if (!QUERY_FLAG(&op->arch->clone,FLAG_UNDEAD)) 00580 if (QUERY_FLAG(op,FLAG_UNDEAD) != QUERY_FLAG(&refop,FLAG_UNDEAD)) { 00581 success=1; 00582 if (flag>0) { 00583 if (op->race) free_string(op->race); 00584 op->race=add_string("undead"); 00585 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE, 00586 "Your lifeforce drains away!", NULL); 00587 } else { 00588 if (op->race) free_string(op->race); 00589 if (op->arch->clone.race) 00590 op->race=add_string(op->arch->clone.race); 00591 else 00592 op->race = NULL; 00593 draw_ext_info(NDI_UNIQUE, 0, op,MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE, 00594 "Your lifeforce returns!", NULL); 00595 } 00596 } 00597 00598 if (QUERY_FLAG(op,FLAG_STEALTH) != QUERY_FLAG(&refop,FLAG_STEALTH)) { 00599 success=1; 00600 DIFF_MSG(flag, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_START, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END, 00601 "You walk more quietly.", 00602 "You walk more noisily."); 00603 } 00604 if (QUERY_FLAG(op,FLAG_MAKE_INVIS) != QUERY_FLAG(&refop,FLAG_MAKE_INVIS)) { 00605 success=1; 00606 DIFF_MSG(flag, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_START, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END, 00607 "You become transparent.", 00608 "You can see yourself."); 00609 } 00610 /* blinded you can tell if more blinded since blinded player has minimal 00611 * vision 00612 */ 00613 if (QUERY_FLAG(tmp,FLAG_BLIND)) { 00614 success=1; 00615 if (flag>0) { 00616 if (QUERY_FLAG(op,FLAG_WIZ)) 00617 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START, 00618 "Your mortal self is blinded.", NULL); 00619 else { 00620 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START, 00621 "You are blinded.", NULL); 00622 SET_FLAG(op,FLAG_BLIND); 00623 if (op->type==PLAYER) 00624 op->contr->do_los=1; 00625 } 00626 } else { 00627 if (QUERY_FLAG(op,FLAG_WIZ)) 00628 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END, 00629 "Your mortal self can now see again.", NULL); 00630 else { 00631 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END, 00632 "Your vision returns.", NULL); 00633 CLEAR_FLAG(op,FLAG_BLIND); 00634 if (op->type==PLAYER) 00635 op->contr->do_los=1; 00636 } 00637 } 00638 } 00639 00640 if (QUERY_FLAG(op,FLAG_SEE_IN_DARK) != QUERY_FLAG(&refop,FLAG_SEE_IN_DARK)) { 00641 success=1; 00642 if (op->type==PLAYER) 00643 op->contr->do_los=1; 00644 DIFF_MSG(flag, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_START, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END, 00645 "Your vision is better in the dark.", 00646 "You see less well in the dark."); 00647 } 00648 00649 if (QUERY_FLAG(op,FLAG_XRAYS) != QUERY_FLAG(&refop,FLAG_XRAYS)) { 00650 success=1; 00651 if (flag>0) { 00652 if (QUERY_FLAG(op,FLAG_WIZ)) 00653 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_START, 00654 "Your vision becomes a little clearer.", NULL); 00655 else { 00656 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_START, 00657 "Everything becomes transparent.", NULL); 00658 if (op->type==PLAYER) 00659 op->contr->do_los=1; 00660 } 00661 } else { 00662 if (QUERY_FLAG(op,FLAG_WIZ)) 00663 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END, 00664 "Your vision becomes a bit out of focus.", NULL); 00665 else { 00666 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END, 00667 "Everything suddenly looks very solid.", NULL); 00668 if (op->type==PLAYER) 00669 op->contr->do_los=1; 00670 } 00671 } 00672 } 00673 00674 if (tmp->stats.luck) { 00675 success=1; 00676 DIFF_MSG(flag*tmp->stats.luck, MSG_TYPE_ATTRIBUTE_STAT_GAIN, MSG_TYPE_ATTRIBUTE_STAT_LOSS, 00677 "You feel more lucky.", 00678 "You feel less lucky."); 00679 } 00680 00681 if (tmp->stats.hp && op->type==PLAYER) { 00682 success=1; 00683 DIFF_MSG(flag*tmp->stats.hp, MSG_TYPE_ATTRIBUTE_STAT_GAIN, MSG_TYPE_ATTRIBUTE_STAT_LOSS, 00684 "You feel much more healthy!", 00685 "You feel much less healthy!"); 00686 } 00687 00688 if (tmp->stats.sp && op->type==PLAYER && tmp->type!=SKILL) { 00689 success=1; 00690 DIFF_MSG(flag*tmp->stats.sp, MSG_TYPE_ATTRIBUTE_STAT_GAIN, MSG_TYPE_ATTRIBUTE_STAT_LOSS, 00691 "You feel one with the powers of magic!", 00692 "You suddenly feel very mundane."); 00693 } 00694 00695 /* for the future when artifacts set this -b.t. */ 00696 if (tmp->stats.grace && op->type==PLAYER) { 00697 success=1; 00698 DIFF_MSG(flag*tmp->stats.grace, MSG_TYPE_ATTRIBUTE_STAT_GAIN, MSG_TYPE_ATTRIBUTE_STAT_LOSS, 00699 "You feel closer to your god!", 00700 "You suddenly feel less holy."); 00701 } 00702 00703 if (tmp->stats.food && op->type==PLAYER) { 00704 success=1; 00705 DIFF_MSG(flag*tmp->stats.food, MSG_TYPE_ATTRIBUTE_STAT_GAIN, MSG_TYPE_ATTRIBUTE_STAT_LOSS, 00706 "You feel your digestion slowing down.", 00707 "You feel your digestion speeding up."); 00708 } 00709 00710 /* Messages for changed resistance */ 00711 for (i=0; i<NROFATTACKS; i++) { 00712 if (i==ATNR_PHYSICAL) continue; /* Don't display about armour */ 00713 00714 if (op->resist[i] != refop.resist[i]) { 00715 success=1; 00716 if (op->resist[i] > refop.resist[i]) 00717 draw_ext_info_format(NDI_UNIQUE|NDI_BLUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN, 00718 "Your resistance to %s rises to %d%%.", 00719 "Your resistance to %s rises to %d%%.", 00720 change_resist_msg[i], op->resist[i]); 00721 else 00722 draw_ext_info_format(NDI_UNIQUE|NDI_BLUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_PROTECTION_LOSS, 00723 "Your resistance to %s drops to %d%%.", 00724 "Your resistance to %s drops to %d%%.", 00725 change_resist_msg[i], op->resist[i]); 00726 } 00727 } 00728 00729 if (tmp->type!=EXPERIENCE && !potion_max) { 00730 for (j=0; j<NUM_STATS; j++) { 00731 if ((i=get_attr_value(&(tmp->stats),j))!=0) { 00732 success=1; 00733 DIFF_MSG(i * flag, MSG_TYPE_ATTRIBUTE_STAT_GAIN, MSG_TYPE_ATTRIBUTE_STAT_LOSS, gain_msg[j], lose_msg[j]); 00734 } 00735 } 00736 } 00737 return success; 00738 } 00739 00748 void drain_stat(object *op) { 00749 drain_specific_stat(op, RANDOM()%NUM_STATS); 00750 } 00751 00760 void drain_specific_stat(object *op, int deplete_stats) { 00761 object *tmp; 00762 archetype *at; 00763 00764 at = find_archetype(ARCH_DEPLETION); 00765 if (!at) { 00766 LOG(llevError, "Couldn't find archetype depletion.\n"); 00767 return; 00768 } else { 00769 tmp = present_arch_in_ob(at, op); 00770 if (!tmp) { 00771 tmp = arch_to_object(at); 00772 tmp = insert_ob_in_ob(tmp, op); 00773 SET_FLAG(tmp,FLAG_APPLIED); 00774 } 00775 } 00776 00777 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_STAT_LOSS, drain_msg[deplete_stats], NULL); 00778 change_attr_value(&tmp->stats, deplete_stats, -1); 00779 fix_object(op); 00780 } 00781 00791 void change_luck(object *op, int value) { 00792 object *tmp; 00793 archetype *at; 00794 int new_luck; 00795 00796 at = find_archetype("luck"); 00797 if (!at) 00798 LOG(llevError, "Couldn't find archetype luck.\n"); 00799 else { 00800 tmp = present_arch_in_ob(at, op); 00801 if (!tmp) { 00802 if (!value) 00803 return; 00804 tmp = arch_to_object(at); 00805 tmp = insert_ob_in_ob(tmp, op); 00806 SET_FLAG(tmp,FLAG_APPLIED); 00807 } 00808 if (value) { 00809 /* Limit the luck value of the bad luck object to +/-100. This 00810 * (arbitrary) value prevents overflows (both in the bad luck object and 00811 * in op itself). 00812 */ 00813 new_luck = tmp->stats.luck+value; 00814 if (new_luck >= -100 && new_luck <= 100) { 00815 op->stats.luck+=value; 00816 tmp->stats.luck = new_luck; 00817 } 00818 } else { 00819 if (!tmp->stats.luck) { 00820 return; 00821 } 00822 /* Randomly change the players luck. Basically, we move it 00823 * back neutral (if greater>0, subtract, otherwise add) 00824 */ 00825 if (RANDOM()%(FABS(tmp->stats.luck)) >= RANDOM()%30) { 00826 int diff = tmp->stats.luck>0?-1:1; 00827 op->stats.luck += diff; 00828 tmp->stats.luck += diff; 00829 } 00830 } 00831 } 00832 } 00833 00840 void remove_statbonus(object *op) { 00841 op->stats.Str -= op->arch->clone.stats.Str; 00842 op->stats.Dex -= op->arch->clone.stats.Dex; 00843 op->stats.Con -= op->arch->clone.stats.Con; 00844 op->stats.Wis -= op->arch->clone.stats.Wis; 00845 op->stats.Pow -= op->arch->clone.stats.Pow; 00846 op->stats.Cha -= op->arch->clone.stats.Cha; 00847 op->stats.Int -= op->arch->clone.stats.Int; 00848 op->contr->orig_stats.Str -= op->arch->clone.stats.Str; 00849 op->contr->orig_stats.Dex -= op->arch->clone.stats.Dex; 00850 op->contr->orig_stats.Con -= op->arch->clone.stats.Con; 00851 op->contr->orig_stats.Wis -= op->arch->clone.stats.Wis; 00852 op->contr->orig_stats.Pow -= op->arch->clone.stats.Pow; 00853 op->contr->orig_stats.Cha -= op->arch->clone.stats.Cha; 00854 op->contr->orig_stats.Int -= op->arch->clone.stats.Int; 00855 } 00856 00863 void add_statbonus(object *op) { 00864 op->stats.Str += op->arch->clone.stats.Str; 00865 op->stats.Dex += op->arch->clone.stats.Dex; 00866 op->stats.Con += op->arch->clone.stats.Con; 00867 op->stats.Wis += op->arch->clone.stats.Wis; 00868 op->stats.Pow += op->arch->clone.stats.Pow; 00869 op->stats.Cha += op->arch->clone.stats.Cha; 00870 op->stats.Int += op->arch->clone.stats.Int; 00871 op->contr->orig_stats.Str += op->arch->clone.stats.Str; 00872 op->contr->orig_stats.Dex += op->arch->clone.stats.Dex; 00873 op->contr->orig_stats.Con += op->arch->clone.stats.Con; 00874 op->contr->orig_stats.Wis += op->arch->clone.stats.Wis; 00875 op->contr->orig_stats.Pow += op->arch->clone.stats.Pow; 00876 op->contr->orig_stats.Cha += op->arch->clone.stats.Cha; 00877 op->contr->orig_stats.Int += op->arch->clone.stats.Int; 00878 } 00879 00900 void fix_object(object *op) { 00901 int i,j; 00902 float f,max=9,added_speed=0,bonus_speed=0, sp_tmp,speed_reduce_from_disease=1; 00903 int weapon_weight=0,weapon_speed=0; 00904 int best_wc=0, best_ac=0, wc=0, ac=0; 00905 int prot[NROFATTACKS], vuln[NROFATTACKS], potion_resist[NROFATTACKS]; 00906 object *grace_obj=NULL,*mana_obj=NULL,*wc_obj=NULL,*tmp; 00907 00908 /* First task is to clear all the values back to their original values */ 00909 if (op->type==PLAYER) { 00910 for (i=0;i<NUM_STATS;i++) { 00911 set_attr_value(&(op->stats),i,get_attr_value(&(op->contr->orig_stats),i)); 00912 } 00913 if (settings.spell_encumbrance == TRUE) 00914 op->contr->encumbrance=0; 00915 00916 op->attacktype=0; 00917 op->contr->digestion = 0; 00918 op->contr->gen_hp = 0; 00919 op->contr->gen_sp = 0; 00920 op->contr->gen_grace = 0; 00921 op->contr->gen_sp_armour = 10; 00922 op->contr->item_power = 0; 00923 00924 /* Don't clobber all the range_ values. range_golem otherwise 00925 * gets reset for no good reason, and we don't want to reset 00926 * range_magic (what spell is readied). These three below 00927 * well get filled in based on what the player has equipped. 00928 */ 00929 op->contr->ranges[range_bow] = NULL; 00930 op->contr->ranges[range_misc] = NULL; 00931 op->contr->ranges[range_skill] = NULL; 00932 } /* If player */ 00933 memcpy(op->body_used, op->body_info, sizeof(op->body_info)); 00934 00935 if (op->slaying!=NULL) { 00936 free_string(op->slaying); 00937 op->slaying=NULL; 00938 } 00939 if (!QUERY_FLAG(op,FLAG_WIZ)) { 00940 CLEAR_FLAG(op, FLAG_XRAYS); 00941 CLEAR_FLAG(op, FLAG_MAKE_INVIS); 00942 } 00943 00944 CLEAR_FLAG(op,FLAG_LIFESAVE); 00945 CLEAR_FLAG(op,FLAG_STEALTH); 00946 CLEAR_FLAG(op,FLAG_BLIND); 00947 if (!QUERY_FLAG(&op->arch->clone, FLAG_REFL_SPELL)) 00948 CLEAR_FLAG(op,FLAG_REFL_SPELL); 00949 if (!QUERY_FLAG(&op->arch->clone, FLAG_REFL_MISSILE)) 00950 CLEAR_FLAG(op,FLAG_REFL_MISSILE); 00951 if (!QUERY_FLAG(&op->arch->clone,FLAG_UNDEAD)) 00952 CLEAR_FLAG(op,FLAG_UNDEAD); 00953 if (!QUERY_FLAG(&op->arch->clone, FLAG_SEE_IN_DARK)) 00954 CLEAR_FLAG(op,FLAG_SEE_IN_DARK); 00955 00956 op->path_attuned=op->arch->clone.path_attuned; 00957 op->path_repelled=op->arch->clone.path_repelled; 00958 op->path_denied=op->arch->clone.path_denied; 00959 op->glow_radius=op->arch->clone.glow_radius; 00960 op->move_type = op->arch->clone.move_type; 00961 op->chosen_skill = NULL; 00962 00963 /* initializing resistances from the values in player/monster's 00964 * archetype clone 00965 */ 00966 memcpy(&op->resist, &op->arch->clone.resist, sizeof(op->resist)); 00967 00968 for (i=0;i<NROFATTACKS;i++) { 00969 if (op->resist[i] > 0) 00970 prot[i]= op->resist[i], vuln[i]=0; 00971 else 00972 vuln[i]= -(op->resist[i]), prot[i]=0; 00973 potion_resist[i]=0; 00974 } 00975 00976 wc=op->arch->clone.stats.wc; 00977 op->stats.dam=op->arch->clone.stats.dam; 00978 00979 /* for players which cannot use armour, they gain AC -1 per 3 levels, 00980 * plus a small amount of physical resist, those poor suckers. ;) 00981 * the fact that maxlevel is factored in could be considered sort of bogus - 00982 * we should probably give them some bonus and cap it off - otherwise, 00983 * basically, if a server updates its max level, these playes may find 00984 * that their protection from physical goes down 00985 */ 00986 if (!QUERY_FLAG(op,FLAG_USE_ARMOUR) && op->type==PLAYER) { 00987 ac=MAX(-10,op->arch->clone.stats.ac - op->level/3); 00988 prot[ATNR_PHYSICAL] += ((100-prot[AT_PHYSICAL])*(80*op->level/settings.max_level))/100; 00989 } else 00990 ac=op->arch->clone.stats.ac; 00991 00992 op->stats.luck=op->arch->clone.stats.luck; 00993 op->speed = op->arch->clone.speed; 00994 00995 /* OK - we've reset most all the objects attributes to sane values. 00996 * now go through and make adjustments for what the player has equipped. 00997 */ 00998 00999 for (tmp=op->inv;tmp!=NULL;tmp=tmp->below) { 01000 /* See note in map.c:update_position about making this additive 01001 * since light sources are never applied, need to put check here. 01002 */ 01003 if (tmp->glow_radius > op->glow_radius) 01004 op->glow_radius=tmp->glow_radius; 01005 01006 /* This happens because apply_potion calls change_abil with the potion 01007 * applied so we can tell the player what chagned. But change_abil 01008 * then calls this function. 01009 */ 01010 if (QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type == POTION) 01011 continue; 01012 01013 /* For some things, we don't care what is equipped */ 01014 if (tmp->type == SKILL) { 01015 /* Want to take the highest skill here. */ 01016 if (IS_MANA_SKILL(tmp->subtype)) { 01017 if (!mana_obj) 01018 mana_obj = tmp; 01019 else if (tmp->level > mana_obj->level) 01020 mana_obj = tmp; 01021 } 01022 if (IS_GRACE_SKILL(tmp->subtype)) { 01023 if (!grace_obj) 01024 grace_obj=tmp; 01025 else if (tmp->level > grace_obj->level) 01026 grace_obj = tmp; 01027 } 01028 } 01029 01030 /* Container objects are not meant to adjust a players, but other applied 01031 * objects need to make adjustments. 01032 * This block should handle all player specific changes 01033 * The check for Praying is a bit of a hack - god given bonuses are put 01034 * in the praying skill, and the player should always get those. 01035 * It also means we need to put in additional checks for applied below, 01036 * because the skill shouldn't count against body positions being used 01037 * up, etc. 01038 */ 01039 if ((QUERY_FLAG(tmp,FLAG_APPLIED) && tmp->type!=CONTAINER && tmp->type!=CLOSE_CON) || 01040 (tmp->type == SKILL && tmp->subtype == SK_PRAYING)) { 01041 if (op->type==PLAYER) { 01042 if (tmp->type == BOW) 01043 op->contr->ranges[range_bow] = tmp; 01044 01045 if (tmp->type == WAND || tmp->type == ROD || tmp->type==HORN) 01046 op->contr->ranges[range_misc] = tmp; 01047 01048 for (i=0;i<NUM_STATS;i++) 01049 change_attr_value(&(op->stats),i,get_attr_value(&(tmp->stats),i)); 01050 01051 /* these are the items that currently can change digestion, regeneration, 01052 * spell point recovery and mana point recovery. Seems sort of an arbitary 01053 * list, but other items store other info into stats array. 01054 */ 01055 if ((tmp->type == EXPERIENCE) || (tmp->type == WEAPON) || 01056 (tmp->type == ARMOUR) || (tmp->type == HELMET) || 01057 (tmp->type == SHIELD) || (tmp->type == RING) || 01058 (tmp->type == BOOTS) || (tmp->type == GLOVES) || 01059 (tmp->type == AMULET) || (tmp->type == GIRDLE) || 01060 (tmp->type == BRACERS) || (tmp->type == CLOAK) || 01061 (tmp->type == DISEASE) || (tmp->type == FORCE) || 01062 (tmp->type == SKILL)) { 01063 op->contr->digestion += tmp->stats.food; 01064 op->contr->gen_hp += tmp->stats.hp; 01065 op->contr->gen_sp += tmp->stats.sp; 01066 op->contr->gen_grace += tmp->stats.grace; 01067 op->contr->gen_sp_armour+= tmp->gen_sp_armour; 01068 op->contr->item_power += tmp->item_power; 01069 } 01070 } /* if this is a player */ 01071 01072 /* Update slots used for items */ 01073 if (QUERY_FLAG(tmp,FLAG_APPLIED)) { 01074 for (i=0; i<NUM_BODY_LOCATIONS; i++) 01075 op->body_used[i] += tmp->body_info[i]; 01076 } 01077 01078 if(tmp->type==SYMPTOM) { 01079 speed_reduce_from_disease = tmp->last_sp / 100.0; 01080 if(speed_reduce_from_disease ==0) 01081 speed_reduce_from_disease = 1; 01082 } 01083 01084 /* Pos. and neg. protections are counted seperate (-> pro/vuln). 01085 * (Negative protections are calculated extactly like positive.) 01086 * Resistance from potions are treated special as well. If there's 01087 * more than one potion-effect, the bigger prot.-value is taken. 01088 */ 01089 if (tmp->type != POTION) { 01090 for (i=0; i<NROFATTACKS; i++) { 01091 /* Potential for cursed potions, in which case we just can use 01092 * a straight MAX, as potion_resist is initialized to zero. 01093 */ 01094 if (tmp->type==POTION_EFFECT) { 01095 if (potion_resist[i]) 01096 potion_resist[i] = MAX(potion_resist[i], tmp->resist[i]); 01097 else 01098 potion_resist[i] = tmp->resist[i]; 01099 } else if (tmp->resist[i] > 0) 01100 prot[i] += ((100-prot[i])*tmp->resist[i])/100; 01101 else if (tmp->resist[i] < 0) 01102 vuln[i] += ((100-vuln[i])*(-tmp->resist[i]))/100; 01103 } 01104 } 01105 01106 /* There may be other things that should not adjust the attacktype */ 01107 if (tmp->type!=BOW && tmp->type != SYMPTOM) 01108 op->attacktype|=tmp->attacktype; 01109 01110 op->path_attuned|=tmp->path_attuned; 01111 op->path_repelled|=tmp->path_repelled; 01112 op->path_denied|=tmp->path_denied; 01113 op->stats.luck+=tmp->stats.luck; 01114 op->move_type |= tmp->move_type; 01115 01116 if (QUERY_FLAG(tmp,FLAG_LIFESAVE)) 01117 SET_FLAG(op,FLAG_LIFESAVE); 01118 if (QUERY_FLAG(tmp,FLAG_REFL_SPELL)) 01119 SET_FLAG(op,FLAG_REFL_SPELL); 01120 if (QUERY_FLAG(tmp,FLAG_REFL_MISSILE)) 01121 SET_FLAG(op,FLAG_REFL_MISSILE); 01122 if (QUERY_FLAG(tmp,FLAG_STEALTH)) 01123 SET_FLAG(op,FLAG_STEALTH); 01124 if (QUERY_FLAG(tmp,FLAG_XRAYS)) 01125 SET_FLAG(op,FLAG_XRAYS); 01126 if (QUERY_FLAG(tmp,FLAG_BLIND)) 01127 SET_FLAG(op,FLAG_BLIND); 01128 if (QUERY_FLAG(tmp,FLAG_SEE_IN_DARK)) 01129 SET_FLAG(op,FLAG_SEE_IN_DARK); 01130 01131 if (QUERY_FLAG(tmp,FLAG_UNDEAD) && !QUERY_FLAG(&op->arch->clone,FLAG_UNDEAD)) 01132 SET_FLAG(op,FLAG_UNDEAD); 01133 01134 if (QUERY_FLAG(tmp,FLAG_MAKE_INVIS)) { 01135 SET_FLAG(op,FLAG_MAKE_INVIS); 01136 op->invisible=1; 01137 } 01138 01139 if (tmp->stats.exp && tmp->type!=SKILL) { 01140 if(tmp->stats.exp > 0) { 01141 added_speed+=(float)tmp->stats.exp/3.0; 01142 bonus_speed+=1.0+(float)tmp->stats.exp/3.0; 01143 } else 01144 added_speed+=(float)tmp->stats.exp; 01145 } 01146 01147 switch (tmp->type) { 01148 /* skills modifying the character -b.t. */ 01149 /* for all skills and skill granting objects */ 01150 case SKILL: 01151 if (!QUERY_FLAG(tmp,FLAG_APPLIED)) 01152 break; 01153 01154 if (IS_COMBAT_SKILL(tmp->subtype)) 01155 wc_obj=tmp; 01156 01157 if (op->chosen_skill) { 01158 LOG(llevDebug, "fix_object, op %s has multiple skills applied\n", op->name); 01159 } 01160 op->chosen_skill = tmp; 01161 if (tmp->stats.dam>0) { /* skill is a 'weapon' */ 01162 if (!QUERY_FLAG(op,FLAG_READY_WEAPON)) 01163 weapon_speed = (int) WEAPON_SPEED(tmp); 01164 if (weapon_speed<0) 01165 weapon_speed = 0; 01166 weapon_weight=tmp->weight; 01167 op->stats.dam+=tmp->stats.dam*(1 + (op->chosen_skill->level/9)); 01168 if (tmp->magic) 01169 op->stats.dam += tmp->magic; 01170 } 01171 if (tmp->stats.wc) 01172 wc-=(tmp->stats.wc+tmp->magic); 01173 01174 if (tmp->slaying!=NULL) { 01175 if (op->slaying != NULL) 01176 free_string(op->slaying); 01177 add_refcount(op->slaying = tmp->slaying); 01178 } 01179 01180 if (tmp->stats.ac) 01181 ac-=(tmp->stats.ac+tmp->magic); 01182 if (settings.spell_encumbrance == TRUE && op->type==PLAYER) 01183 op->contr->encumbrance+=(int)3*tmp->weight/1000; 01184 if (op->type == PLAYER) 01185 op->contr->ranges[range_skill] = op; 01186 break; 01187 01188 case SKILL_TOOL: 01189 if (op->chosen_skill) { 01190 LOG(llevDebug, "fix_object, op %s has multiple skills applied\n", op->name); 01191 } 01192 op->chosen_skill = tmp; 01193 if (op->type == PLAYER) 01194 op->contr->ranges[range_skill] = op; 01195 break; 01196 01197 case SHIELD: 01198 if (settings.spell_encumbrance == TRUE && op->type==PLAYER) 01199 op->contr->encumbrance+=(int)tmp->weight/2000; 01200 case RING: 01201 case AMULET: 01202 case GIRDLE: 01203 case HELMET: 01204 case BOOTS: 01205 case GLOVES: 01206 case CLOAK: 01207 if (tmp->stats.wc) 01208 wc-=(tmp->stats.wc+tmp->magic); 01209 if (tmp->stats.dam) 01210 op->stats.dam+=(tmp->stats.dam+tmp->magic); 01211 if (tmp->stats.ac) 01212 ac-=(tmp->stats.ac+tmp->magic); 01213 break; 01214 01215 case WEAPON: 01216 wc-=(tmp->stats.wc+tmp->magic); 01217 if (tmp->stats.ac&&tmp->stats.ac+tmp->magic>0) 01218 ac-=tmp->stats.ac+tmp->magic; 01219 op->stats.dam+=(tmp->stats.dam+tmp->magic); 01220 weapon_weight=tmp->weight; 01221 weapon_speed=((int)WEAPON_SPEED(tmp)*2-tmp->magic)/2; 01222 if (weapon_speed<0) 01223 weapon_speed=0; 01224 if (tmp->slaying!=NULL) { 01225 if (op->slaying != NULL) 01226 free_string(op->slaying); 01227 add_refcount(op->slaying = tmp->slaying); 01228 } 01229 /* If there is desire that two handed weapons should do 01230 * extra strength damage, this is where the code should 01231 * go. 01232 */ 01233 op->current_weapon = tmp; 01234 if (settings.spell_encumbrance == TRUE && op->type==PLAYER) 01235 op->contr->encumbrance+=(int)3*tmp->weight/1000; 01236 break; 01237 01238 case ARMOUR: /* Only the best of these three are used: */ 01239 if (settings.spell_encumbrance == TRUE && op->type==PLAYER) 01240 op->contr->encumbrance+=(int)tmp->weight/1000; 01241 01242 case BRACERS: 01243 case FORCE: 01244 if (tmp->stats.wc) { 01245 if (best_wc<tmp->stats.wc+tmp->magic) { 01246 wc+=best_wc; 01247 best_wc=tmp->stats.wc+tmp->magic; 01248 } else 01249 wc+=tmp->stats.wc+tmp->magic; 01250 } 01251 if (tmp->stats.ac) { 01252 if (best_ac<tmp->stats.ac+tmp->magic) { 01253 ac+=best_ac; /* Remove last bonus */ 01254 best_ac=tmp->stats.ac+tmp->magic; 01255 } else /* To nullify the below effect */ 01256 ac+=tmp->stats.ac+tmp->magic; 01257 } 01258 if (tmp->stats.dam && tmp->type == BRACERS) 01259 op->stats.dam+=(tmp->stats.dam+tmp->magic); 01260 if (tmp->stats.wc) 01261 wc-=(tmp->stats.wc+tmp->magic); 01262 if (tmp->stats.ac) 01263 ac-=(tmp->stats.ac+tmp->magic); 01264 if (ARMOUR_SPEED(tmp) && ARMOUR_SPEED(tmp)/10.0<max) 01265 max=ARMOUR_SPEED(tmp)/10.0; 01266 break; 01267 } /* switch tmp->type */ 01268 } /* item is equipped */ 01269 } /* for loop of items */ 01270 01271 /* We've gone through all the objects the player has equipped. For many things, we 01272 * have generated intermediate values which we now need to assign. 01273 */ 01274 01275 /* 'total resistance = total protections - total vulnerabilities'. 01276 * If there is an uncursed potion in effect, granting more protection 01277 * than that, we take: 'total resistance = resistance from potion'. 01278 * If there is a cursed (and no uncursed) potion in effect, we take 01279 * 'total resistance = vulnerability from cursed potion'. 01280 */ 01281 for (i=0; i<NROFATTACKS; i++) { 01282 op->resist[i] = prot[i] - vuln[i]; 01283 if (potion_resist[i] && ((potion_resist[i] > op->resist[i]) || 01284 (potion_resist[i] < 0))) 01285 op->resist[i] = potion_resist[i]; 01286 } 01287 01288 /* Figure out the players sp/mana/hp totals. */ 01289 if (op->type==PLAYER) { 01290 int pl_level; 01291 01292 check_stat_bounds(&(op->stats)); 01293 pl_level=op->level; 01294 01295 if (pl_level<1) 01296 pl_level=1; /* safety, we should always get 1 levels worth of hp! */ 01297 01298 /* You basically get half a con bonus/level. But we do take into account rounding, 01299 * so if your bonus is 7, you still get 7 worth of bonus every 2 levels. 01300 */ 01301 for (i=1,op->stats.maxhp=0;i<=pl_level&&i<=10;i++) { 01302 j = op->contr->levhp[i] + con_bonus[op->stats.Con] / 2; 01303 if (i%2 && con_bonus[op->stats.Con]%2) { 01304 if (con_bonus[op->stats.Con]>0) 01305 j++; 01306 else 01307 j--; 01308 } 01309 op->stats.maxhp+=j>1?j:1; /* always get at least 1 hp/level */ 01310 } 01311 01312 for (i=11;i<=op->level;i++) 01313 op->stats.maxhp+=2; 01314 01315 if (op->stats.hp>op->stats.maxhp) 01316 op->stats.hp=op->stats.maxhp; 01317 01318 /* Sp gain is controlled by the level of the player's 01319 * relevant experience object (mana_obj, see above) 01320 */ 01321 /* following happen when skills system is not used */ 01322 if (!mana_obj) 01323 mana_obj = op; 01324 if (!grace_obj) 01325 grace_obj = op; 01326 01327 /* set maxsp */ 01328 if (!mana_obj || !mana_obj->level || op->type!=PLAYER) 01329 mana_obj = op; 01330 01331 if (mana_obj == op && op->type == PLAYER) { 01332 op->stats.maxsp = 1; 01333 } else { 01334 sp_tmp=0.0; 01335 for (i=1;i<=mana_obj->level&&i<=10;i++) { 01336 float stmp; 01337 01338 /* Got some extra bonus at first level */ 01339 if (i<2) { 01340 stmp = op->contr->levsp[i] +((2.0 * (float)sp_bonus[op->stats.Pow] + 01341 (float)sp_bonus[op->stats.Int])/6.0); 01342 } else { 01343 stmp=(float)op->contr->levsp[i] 01344 +(2.0 * (float)sp_bonus[op->stats.Pow] + 01345 (float)sp_bonus[op->stats.Int])/12.0; 01346 } 01347 if (stmp<1.0) 01348 stmp=1.0; 01349 sp_tmp+=stmp; 01350 } 01351 op->stats.maxsp=(int)sp_tmp; 01352 01353 for (i=11;i<=mana_obj->level;i++) 01354 op->stats.maxsp+=2; 01355 } 01356 01357 /* Characters can get their sp supercharged via rune of transferrance */ 01358 if (op->stats.sp>op->stats.maxsp*2) 01359 op->stats.sp=op->stats.maxsp*2; 01360 01361 /* set maxgrace, notice 3-4 lines below it depends on both Wis and Pow */ 01362 if (!grace_obj || !grace_obj->level || op->type!=PLAYER) 01363 grace_obj = op; 01364 01365 if (grace_obj == op && op->type == PLAYER) { 01366 op->stats.maxgrace = 1; 01367 } else { 01368 /* store grace in a float - this way, the divisions below don't create 01369 * big jumps when you go from level to level - with int's, it then 01370 * becomes big jumps when the sums of the bonuses jump to the next 01371 * step of 8 - with floats, even fractional ones are useful. 01372 */ 01373 sp_tmp=0.0; 01374 for (i=1,op->stats.maxgrace=0;i<=grace_obj->level&&i<=10;i++) { 01375 float grace_tmp=0.0; 01376 01377 /* Got some extra bonus at first level */ 01378 if (i<2) { 01379 grace_tmp = op->contr->levgrace[i]+(((float)grace_bonus[op->stats.Pow] + 01380 2.0 * (float)grace_bonus[op->stats.Wis])/6.0); 01381 } else { 01382 grace_tmp=(float)op->contr->levgrace[i] 01383 +((float)grace_bonus[op->stats.Pow] + 01384 2.0 * (float)grace_bonus[op->stats.Wis])/12.0; 01385 } 01386 if (grace_tmp<1.0) 01387 grace_tmp=1.0; 01388 sp_tmp+=grace_tmp; 01389 } 01390 op->stats.maxgrace=(int)sp_tmp; 01391 01392 /* two grace points per level after 11 */ 01393 for (i=11;i<=grace_obj->level;i++) 01394 op->stats.maxgrace+=2; 01395 } 01396 /* No limit on grace vs maxgrace */ 01397 01398 if (op->contr->braced) { 01399 ac+=2; 01400 wc+=4; 01401 } else 01402 ac-=dex_bonus[op->stats.Dex]; 01403 01404 /* In new exp/skills system, wc bonuses are related to 01405 * the players level in a relevant exp object (wc_obj) 01406 * not the general player level -b.t. 01407 * I changed this slightly so that wc bonuses are better 01408 * than before. This is to balance out the fact that 01409 * the player no longer gets a personal weapon w/ 1 01410 * improvement every level, now its fighterlevel/5. So 01411 * we give the player a bonus here in wc and dam 01412 * to make up for the change. Note that I left the 01413 * monster bonus the same as before. -b.t. 01414 */ 01415 01416 if (op->type==PLAYER && wc_obj && wc_obj->level>1) { 01417 wc-=(wc_obj->level+thaco_bonus[op->stats.Str]); 01418 for(i=1;i<wc_obj->level;i++) { 01419 /* addtional wc every 6 levels */ 01420 if(!(i%6)) wc--; 01421 /* addtional dam every 4 levels. */ 01422 if(!(i%4) && (dam_bonus[op->stats.Str]>=0)) 01423 op->stats.dam+=(1+(dam_bonus[op->stats.Str]/5)); 01424 } 01425 } else 01426 wc-=(op->level+thaco_bonus[op->stats.Str]); 01427 01428 op->stats.dam+=dam_bonus[op->stats.Str]; 01429 01430 if(op->stats.dam<1) 01431 op->stats.dam=1; 01432 01433 op->speed = 1.0 + speed_bonus[op->stats.Dex]; 01434 if (settings.search_items && op->contr->search_str[0]) 01435 op->speed -= 1; 01436 if (op->attacktype==0) 01437 op->attacktype=op->arch->clone.attacktype; 01438 01439 } /* End if player */ 01440 01441 /* Max is determined by armour */ 01442 if (op->speed>max) 01443 op->speed=max; 01444 01445 if(added_speed>=0) 01446 op->speed+=added_speed/10.0; 01447 else /* Something wrong here...: */ 01448 op->speed /= (float)(1.0-added_speed); 01449 01450 op->speed+=bonus_speed/10.0; /* Not affected by limits */ 01451 01452 if(op->type == PLAYER) { 01453 /* f is a number the represents the number of kg above (positive num) 01454 * or below (negative number) that the player is carrying. If above 01455 * weight limit, then player suffers a speed reduction based on how 01456 * much above he is, and what is max carry is 01457 */ 01458 f=(op->carrying/1000)-max_carry[op->stats.Str]; 01459 if(f>0) 01460 op->speed=op->speed/(1.0+f/max_carry[op->stats.Str]); 01461 } 01462 01463 /* Put a lower limit on speed. Note with this speed, you move once every 01464 * 100 ticks or so. This amounts to once every 12 seconds of realtime. 01465 */ 01466 op->speed = op->speed * speed_reduce_from_disease; 01467 01468 if (op->speed<0.01 && op->type==PLAYER) 01469 op->speed=0.01; 01470 01471 if(op->type == PLAYER) { 01472 float M,W,s,D,K,S,M2; 01473 01474 /* (This formula was made by vidarl@ifi.uio.no) 01475 * Note that we never used these values again - basically 01476 * all of these could be subbed into one big equation, but 01477 * that would just be a real pain to read. 01478 */ 01479 M=(max_carry[op->stats.Str]-121)/121.0; 01480 M2=max_carry[op->stats.Str]/100.0; 01481 W=weapon_weight/20000.0; 01482 s=2-weapon_speed/10.0; 01483 D=(op->stats.Dex-14)/14.0; 01484 K=1 + M/3.0 - W/(3*M2) + op->speed/5.0 + D/2.0; 01485 K*=(4+op->level)/(float)(6+op->level)*1.2; 01486 if(K<=0) 01487 K=0.01; 01488 S=op->speed/(K*s); 01489 op->contr->weapon_sp=S; 01490 } 01491 /* I want to limit the power of small monsters with big weapons: */ 01492 if (op->type!=PLAYER&&op->arch!=NULL&& 01493 op->stats.dam>op->arch->clone.stats.dam*3) 01494 op->stats.dam=op->arch->clone.stats.dam*3; 01495 01496 /* Prevent overflows of wc - best you can get is ABS(120) - this 01497 * should be more than enough - remember, AC is also in 8 bits, 01498 * so its value is the same. 01499 */ 01500 if (wc>120) 01501 wc=120; 01502 else if (wc<-120) 01503 wc=-120; 01504 op->stats.wc=wc; 01505 01506 if (ac>120) 01507 ac=120; 01508 else if (ac<-120) 01509 ac=-120; 01510 op->stats.ac=ac; 01511 01512 /* if for some reason the creature doesn't have any move type, 01513 * give them walking as a default. 01514 * The second case is a special case - to more closely mimic the 01515 * old behaviour - if your flying, your not walking - just 01516 * one or the other. 01517 */ 01518 if (op->move_type == 0) 01519 op->move_type = MOVE_WALK; 01520 else if (op->move_type & (MOVE_FLY_LOW | MOVE_FLY_HIGH)) 01521 op->move_type &= ~MOVE_WALK; 01522 01523 update_ob_speed(op); 01524 01525 /* It is quite possible that a player's spell costing might have changed, 01526 * so we will check that now. 01527 */ 01528 if (op->type == PLAYER) esrv_update_spells(op->contr); 01529 } 01530 01543 int allowed_class(const object *op) { 01544 return op->stats.Dex>0&&op->stats.Str>0&&op->stats.Con>0&& 01545 op->stats.Int>0&&op->stats.Wis>0&&op->stats.Pow>0&& 01546 op->stats.Cha>0; 01547 } 01548 01566 void set_dragon_name(object *pl, const object *abil, const object *skin) { 01567 int atnr=-1; /* attacknumber of highest level */ 01568 int level=0; /* highest level */ 01569 int i; 01570 01571 /* Perhaps do something more clever? */ 01572 if (!abil || !skin) return; 01573 01574 /* first, look for the highest level */ 01575 for (i=0; i<NROFATTACKS; i++) { 01576 if (atnr_is_dragon_enabled(i) && 01577 (atnr==-1 || abil->resist[i] > abil->resist[atnr])) { 01578 level = abil->resist[i]; 01579 atnr = i; 01580 } 01581 } 01582 01583 /* now if there are equals at highest level, pick the one with focus, 01584 or else at random */ 01585 if (atnr_is_dragon_enabled(abil->stats.exp) && 01586 abil->resist[abil->stats.exp] >= level) 01587 atnr = abil->stats.exp; 01588 01589 level = (int)(level/5.); 01590 01591 /* now set the new title */ 01592 if (pl->contr != NULL) { 01593 if (level == 0) 01594 snprintf(pl->contr->title, sizeof(pl->contr->title), "%s hatchling", attacks[atnr]); 01595 else if (level == 1) 01596 snprintf(pl->contr->title, sizeof(pl->contr->title), "%s wyrm", attacks[atnr]); 01597 else if (level == 2) 01598 snprintf(pl->contr->title, sizeof(pl->contr->title), "%s wyvern", attacks[atnr]); 01599 else if (level == 3) 01600 snprintf(pl->contr->title, sizeof(pl->contr->title), "%s dragon", attacks[atnr]); 01601 else { 01602 /* special titles for extra high resistance! */ 01603 if (skin->resist[atnr] > 80) 01604 snprintf(pl->contr->title, sizeof(pl->contr->title), "legendary %s dragon", attacks[atnr]); 01605 else if (skin->resist[atnr] > 50) 01606 snprintf(pl->contr->title, sizeof(pl->contr->title), "ancient %s dragon", attacks[atnr]); 01607 else 01608 snprintf(pl->contr->title, sizeof(pl->contr->title), "big %s dragon", attacks[atnr]); 01609 } 01610 } 01611 01612 pl->contr->own_title[0] = '\0'; 01613 } 01614 01623 void dragon_level_gain(object *who) { 01624 object *abil = NULL; /* pointer to dragon ability force*/ 01625 object *skin = NULL; /* pointer to dragon skin force*/ 01626 object *tmp = NULL; /* tmp. object */ 01627 01628 /* now grab the 'dragon_ability'-forces from the player's inventory */ 01629 for (tmp=who->inv; tmp!=NULL; tmp=tmp->below) { 01630 if (tmp->type == FORCE) { 01631 if (strcmp(tmp->arch->name, "dragon_ability_force")==0) 01632 abil = tmp; 01633 if (strcmp(tmp->arch->name, "dragon_skin_force")==0) 01634 skin = tmp; 01635 } 01636 } 01637 /* if the force is missing -> bail out */ 01638 if (abil == NULL) return; 01639 01640 /* The ability_force keeps track of maximum level ever achieved. 01641 * New abilties can only be gained by surpassing this max level 01642 */ 01643 if (who->level > abil->level) { 01644 /* increase our focused ability */ 01645 abil->resist[abil->stats.exp]++; 01646 01647 if (abil->resist[abil->stats.exp]>0 && abil->resist[abil->stats.exp]%5 == 0) { 01648 /* time to hand out a new ability-gift */ 01649 dragon_ability_gain(who, (int)abil->stats.exp, 01650 (int)((1+abil->resist[abil->stats.exp])/5.)); 01651 } 01652 01653 if (abil->last_eat > 0 && atnr_is_dragon_enabled(abil->last_eat)) { 01654 /* apply new ability focus */ 01655 draw_ext_info_format(NDI_UNIQUE|NDI_BLUE, 0, who, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE, 01656 "Your metabolism now focuses on %s!", 01657 "Your metabolism now focuses on %s!", 01658 change_resist_msg[abil->last_eat]); 01659 01660 abil->stats.exp = abil->last_eat; 01661 abil->last_eat = 0; 01662 } 01663 01664 abil->level = who->level; 01665 } 01666 01667 /* last but not least, set the new title for the dragon */ 01668 set_dragon_name(who, abil, skin); 01669 } 01670 01689 object *give_skill_by_name(object *op, const char *skill_name) { 01690 object *skill_obj; 01691 archetype *skill_arch; 01692 01693 skill_arch = get_archetype_by_skill_name(skill_name, SKILL); 01694 if (!skill_arch) { 01695 LOG(llevError, "add_player_exp: couldn't find skill %s\n", skill_name); 01696 return NULL; 01697 } 01698 skill_obj = arch_to_object(skill_arch); /* never returns NULL. */ 01699 01700 /* clear the flag - exp goes into this bucket, but player 01701 * still doesn't know it. 01702 */ 01703 CLEAR_FLAG(skill_obj, FLAG_CAN_USE_SKILL); 01704 skill_obj->stats.exp = 0; 01705 skill_obj->level = 1; 01706 insert_ob_in_ob(skill_obj, op); 01707 if (op->contr) { 01708 op->contr->last_skill_ob[skill_obj->subtype] = skill_obj; 01709 op->contr->last_skill_exp[skill_obj->subtype] = -1; 01710 } 01711 return skill_obj; 01712 } 01713 01714 01731 void player_lvl_adj(object *who, object *op) { 01732 char buf[MAX_BUF]; 01733 01734 if (!op) /* when rolling stats */ 01735 op = who; 01736 01737 if (op->level < settings.max_level && op->stats.exp >= level_exp(op->level+1,who->expmul)) { 01738 op->level++; 01739 01740 if (op != NULL && op == who && op->stats.exp > 1 && is_dragon_pl(who)) 01741 dragon_level_gain(who); 01742 01743 /* Only roll these if it is the player (who) that gained the level */ 01744 if (who && op==who && (who->level < 11) && who->type==PLAYER) { 01745 who->contr->levhp[who->level] = die_roll(2, 4, who, PREFER_HIGH)+1; 01746 who->contr->levsp[who->level] = die_roll(2, 3, who, PREFER_HIGH); 01747 who->contr->levgrace[who->level]=die_roll(2, 2, who, PREFER_HIGH)-1; 01748 } 01749 01750 if (who) 01751 fix_object(who); 01752 if (op->level>1) { 01753 if (op->type!=PLAYER) 01754 snprintf(buf, sizeof(buf), "You are now level %d in the %s skill.",op->level,op->name); 01755 else 01756 snprintf(buf, sizeof(buf), "You are now level %d.",op->level); 01757 01758 if (who) 01759 draw_ext_info(NDI_UNIQUE|NDI_RED, 0, who, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_LEVEL_GAIN,buf, buf); 01760 } 01761 player_lvl_adj(who,op); /* To increase more levels */ 01762 } else if (op->level>1 && op->stats.exp<level_exp(op->level,who->expmul)) { 01763 op->level--; 01764 if (who) 01765 fix_object(who); 01766 if (op->type!=PLAYER) { 01767 if (who) 01768 draw_ext_info_format(NDI_UNIQUE|NDI_RED, 0, who, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_LEVEL_LOSS, 01769 "You are now level %d in the %s skill.", 01770 "You are now level %d in the %s skill.", 01771 op->level,op->name); 01772 } 01773 player_lvl_adj(who,op); /* To decrease more levels */ 01774 } 01775 /* check if the spell data has changed */ 01776 esrv_update_spells(who->contr); 01777 } 01778 01788 sint64 level_exp(int level,double expmul) { 01789 if (level > settings.max_level) 01790 return expmul * levels[settings.max_level]; 01791 return expmul * levels[level]; 01792 } 01793 01804 void calc_perm_exp(object *op) { 01805 sint64 p_exp_min; 01806 01807 /* Ensure that our permanent experience minimum is met. 01808 * permenent_exp_ratio is an integer percentage, we divide by 100 01809 * to get the fraction */ 01810 p_exp_min = settings.permanent_exp_ratio*op->stats.exp/100; 01811 01812 if (op->perm_exp < p_exp_min) 01813 op->perm_exp = p_exp_min; 01814 01815 /* Cap permanent experience. */ 01816 if (op->perm_exp < 0) 01817 op->perm_exp = 0; 01818 else if (op->perm_exp > MAX_EXPERIENCE) 01819 op->perm_exp = MAX_EXPERIENCE; 01820 } 01821 01822 01837 static void add_player_exp(object *op, sint64 exp, const char *skill_name, int flag) { 01838 object *skill_obj=NULL; 01839 sint64 limit, exp_to_add; 01840 int i; 01841 01842 /* prevents some forms of abuse. */ 01843 if (op->contr->braced) exp=exp/5; 01844 01845 /* Try to find the matching skill. 01846 * We do a shortcut/time saving mechanism first - see if it matches 01847 * chosen_skill. This means we don't need to search through 01848 * the players inventory. 01849 */ 01850 if (skill_name) { 01851 if (op->chosen_skill && op->chosen_skill->type == SKILL && 01852 !strcmp(skill_name, op->chosen_skill->skill)) 01853 skill_obj = op->chosen_skill; 01854 else { 01855 for (i=0; i<NUM_SKILLS; i++) 01856 if (op->contr->last_skill_ob[i] && 01857 !strcmp(op->contr->last_skill_ob[i]->skill, skill_name)) { 01858 skill_obj = op->contr->last_skill_ob[i]; 01859 break; 01860 } 01861 01862 /* Player doesn't have the skill. Check to see what to do, and give 01863 * it to the player if necessary 01864 */ 01865 if (!skill_obj) { 01866 if (flag == SK_EXP_NONE) return; 01867 else if (flag == SK_EXP_ADD_SKILL) 01868 skill_obj = give_skill_by_name(op, skill_name); 01869 } 01870 } 01871 } 01872 01873 /* Basically, you can never gain more experience in one shot 01874 * than half what you need to gain for next level. 01875 */ 01876 exp_to_add = exp; 01877 limit=(levels[op->level+1]-levels[op->level])/2; 01878 if (exp_to_add > limit) exp_to_add=limit; 01879 01880 ADD_EXP(op->stats.exp, (float) exp_to_add * (skill_obj? skill_obj->expmul:1)); 01881 if (settings.permanent_exp_ratio) { 01882 ADD_EXP(op->perm_exp, (float) exp_to_add * PERM_EXP_GAIN_RATIO * (skill_obj? skill_obj->expmul:1)); 01883 calc_perm_exp(op); 01884 } 01885 01886 player_lvl_adj(op,NULL); 01887 if (skill_obj) { 01888 exp_to_add = exp; 01889 limit=(levels[skill_obj->level+1]-levels[skill_obj->level])/2; 01890 if (exp_to_add > limit) exp_to_add=limit; 01891 ADD_EXP(skill_obj->stats.exp, exp_to_add); 01892 if (settings.permanent_exp_ratio) { 01893 skill_obj->perm_exp += exp_to_add * PERM_EXP_GAIN_RATIO; 01894 calc_perm_exp(skill_obj); 01895 } 01896 player_lvl_adj(op,skill_obj); 01897 } 01898 } 01899 01915 sint64 check_exp_loss(const object *op, sint64 exp) { 01916 sint64 del_exp; 01917 01918 if (exp > op->stats.exp) exp = op->stats.exp; 01919 if (settings.permanent_exp_ratio) { 01920 del_exp = (op->stats.exp - op->perm_exp) * PERM_EXP_MAX_LOSS_RATIO; 01921 if (del_exp < 0) del_exp = 0; 01922 if (exp > del_exp) exp=del_exp; 01923 } 01924 return exp; 01925 } 01926 01937 sint64 check_exp_adjust(const object *op, sint64 exp) { 01938 if (exp<0) return check_exp_loss(op, exp); 01939 else return MIN(exp, MAX_EXPERIENCE - op->stats.exp); 01940 } 01941 01942 01965 static void subtract_player_exp(object *op, sint64 exp, const char *skill, int flag) { 01966 float fraction = (float) exp/(float) op->stats.exp; 01967 object *tmp; 01968 sint64 del_exp; 01969 01970 for (tmp=op->inv;tmp;tmp=tmp->below) 01971 if (tmp->type==SKILL && tmp->stats.exp) { 01972 if (flag == SK_SUBTRACT_SKILL_EXP && skill && !strcmp(tmp->skill, skill)) { 01973 del_exp = check_exp_loss(tmp, exp); 01974 tmp->stats.exp -= del_exp; 01975 player_lvl_adj(op, tmp); 01976 } else if (flag != SK_SUBTRACT_SKILL_EXP) { 01977 /* only want to process other skills if we are not trying 01978 * to match a specific skill. 01979 */ 01980 del_exp = check_exp_loss(tmp, tmp->stats.exp * fraction); 01981 tmp->stats.exp -= del_exp; 01982 player_lvl_adj(op, tmp); 01983 } 01984 } 01985 if (flag != SK_SUBTRACT_SKILL_EXP) { 01986 del_exp = check_exp_loss(op, exp); 01987 op->stats.exp -= del_exp; 01988 player_lvl_adj(op,NULL); 01989 } 01990 } 01991 01992 01993 02015 void change_exp(object *op, sint64 exp, const char *skill_name, int flag) { 02016 02017 #ifdef EXP_DEBUG 02018 char name[MAX_BUF]; 02019 query_name(op, name, MAX_BUF); 02020 #ifndef WIN32 02021 LOG(llevDebug,"change_exp() called for %s, exp = %lld\n",name,exp); 02022 #else 02023 LOG(llevDebug,"change_exp() called for %s, exp = %I64d\n",name,exp); 02024 #endif 02025 #endif 02026 02027 /* safety */ 02028 if (!op) { 02029 LOG(llevError,"change_exp() called for null object!\n"); 02030 return; 02031 } 02032 02033 /* if no change in exp, just return - most of the below code 02034 * won't do anything if the value is 0 anyways. 02035 */ 02036 if (exp == 0) return; 02037 02038 /* Monsters are easy - we just adjust their exp - we 02039 * don't adjust level, since in most cases it is unrelated to 02040 * the exp they have - the monsters exp represents what its 02041 * worth. 02042 */ 02043 if (op->type != PLAYER) { 02044 /* Sanity check */ 02045 if (!QUERY_FLAG(op, FLAG_ALIVE)) return; 02046 02047 /* reset exp to max allowed value. We subtract from 02048 * MAX_EXPERIENCE to prevent overflows. If the player somehow has 02049 * more than max exp, just return. 02050 */ 02051 if (exp > 0 && (op->stats.exp > (MAX_EXPERIENCE - exp))) { 02052 exp = MAX_EXPERIENCE - op->stats.exp; 02053 if (exp < 0) return; 02054 } 02055 02056 op->stats.exp += exp; 02057 } else { /* Players only */ 02058 if (exp>0) 02059 add_player_exp(op, exp, skill_name, flag); 02060 else 02061 subtract_player_exp(op, FABS(exp), skill_name, flag); 02062 } 02063 } 02064 02073 void apply_death_exp_penalty(object *op) { 02074 object *tmp; 02075 sint64 loss; 02076 sint64 percentage_loss; /* defined by the setting 'death_penalty_percent' */ 02077 sint64 level_loss; /* defined by the setting 'death_penalty_levels */ 02078 02079 for (tmp=op->inv;tmp;tmp=tmp->below) 02080 if (tmp->type==SKILL && tmp->stats.exp) { 02081 02082 percentage_loss = tmp->stats.exp * settings.death_penalty_ratio/100; 02083 level_loss = tmp->stats.exp - levels[MAX(0,tmp->level - settings.death_penalty_level)]; 02084 02085 /* With the revised exp system, you can get cases where 02086 * losing several levels would still require that you have more 02087 * exp than you currently have - this is true if the levels 02088 * tables is a lot harder. 02089 */ 02090 if (level_loss < 0) level_loss = 0; 02091 02092 loss = check_exp_loss(tmp, MIN(level_loss, percentage_loss)); 02093 02094 tmp->stats.exp -= loss; 02095 player_lvl_adj(op,tmp); 02096 } 02097 02098 percentage_loss = op->stats.exp * settings.death_penalty_ratio/100; 02099 level_loss = op->stats.exp - levels[MAX(0,op->level - settings.death_penalty_level)]; 02100 if (level_loss < 0) level_loss = 0; 02101 loss = check_exp_loss(op, MIN(level_loss, percentage_loss)); 02102 02103 op->stats.exp -= loss; 02104 player_lvl_adj(op,NULL); 02105 } 02106 02121 int did_make_save(const object *op, int level, int bonus) { 02122 if (level > MAX_SAVE_LEVEL) level = MAX_SAVE_LEVEL; 02123 02124 if ((random_roll(1, 20, op, PREFER_HIGH) + bonus) < savethrow[level]) 02125 return 0; 02126 return 1; 02127 } 02128 02150 void share_exp(object *op, sint64 exp, const char *skill, int flag) { 02151 int shares=0,count=0; 02152 player *pl; 02153 partylist *party; 02154 02155 if (op->type!=PLAYER || op->contr->party==NULL) { 02156 change_exp(op,exp, skill, 0); 02157 return; 02158 } 02159 02160 party = op->contr->party; 02161 02162 for (pl=first_player;pl!=NULL;pl=pl->next) { 02163 if (party && pl->ob->contr->party==party && on_same_map(pl->ob, op)) { 02164 count++; 02165 shares += (pl->ob->level+4); 02166 } 02167 } 02168 if (count==1 || shares>exp) 02169 change_exp(op,exp, skill, flag); 02170 else { 02171 sint64 share=exp/shares,given=0,nexp; 02172 for (pl=first_player;pl!=NULL;pl=pl->next) { 02173 if (party && pl->ob->contr->party==party && on_same_map(pl->ob, op)) { 02174 nexp=(pl->ob->level+4)*share; 02175 change_exp(pl->ob,nexp, skill, SK_EXP_TOTAL); 02176 given+=nexp; 02177 } 02178 } 02179 exp-=given; 02180 /* give any remainder to the player */ 02181 change_exp(op,exp, skill, flag); 02182 } 02183 }