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
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,
00096 5.0, 4.5, 4.0, 3.5, 3.0,
00097 2.9, 2.8, 2.7, 2.6, 2.5,
00098 2.4, 2.3, 2.2, 2.1, 2.0,
00099 1.95, 1.90, 1.85, 1.80, 1.75,
00100 1.70, 1.65, 1.60, 1.55, 1.50
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
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,
00145 250000,300000,350000,400000,500000,
00146 600000,700000,800000,900000,1000000,
00147 1100000,1200000,1300000,1400000,1500000,
00148 1650000,1800000,1950000,2100000,2250000,
00149 2400000,2550000,2700000,2850000,3000000,
00150 3250000,3500000,3750000,4000000,4500000
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
00449
00450
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
00464 nstat = flag*i + ostat;
00465
00466
00467
00468
00469
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
00480 potion_max = 1;
00481 }
00482 }
00483
00484
00485
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 }
00491 }
00492
00493
00494
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
00502
00503
00504 op->move_type &= ~tmp->move_type;
00505 }
00506
00507
00508
00509
00510
00511 fix_object(op);
00512
00513
00514
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
00541
00542
00543
00544
00545
00546 if (tmp->move_type && op->move_type != refop.move_type) {
00547 success=1;
00548
00549
00550
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
00560
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
00573 check_move_on(op, op);
00574 }
00575
00576
00577
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
00611
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
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
00711 for (i=0; i<NROFATTACKS; i++) {
00712 if (i==ATNR_PHYSICAL) continue;
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
00810
00811
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
00823
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
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
00925
00926
00927
00928
00929 op->contr->ranges[range_bow] = NULL;
00930 op->contr->ranges[range_misc] = NULL;
00931 op->contr->ranges[range_skill] = NULL;
00932 }
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
00964
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
00980
00981
00982
00983
00984
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
00996
00997
00998
00999 for (tmp=op->inv;tmp!=NULL;tmp=tmp->below) {
01000
01001
01002
01003 if (tmp->glow_radius > op->glow_radius)
01004 op->glow_radius=tmp->glow_radius;
01005
01006
01007
01008
01009
01010 if (QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type == POTION)
01011 continue;
01012
01013
01014 if (tmp->type == SKILL) {
01015
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
01031
01032
01033
01034
01035
01036
01037
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
01052
01053
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 }
01071
01072
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
01085
01086
01087
01088
01089 if (tmp->type != POTION) {
01090 for (i=0; i<NROFATTACKS; i++) {
01091
01092
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
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
01149
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) {
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
01230
01231
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:
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;
01254 best_ac=tmp->stats.ac+tmp->magic;
01255 } else
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 }
01268 }
01269 }
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
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
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;
01297
01298
01299
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;
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
01319
01320
01321
01322 if (!mana_obj)
01323 mana_obj = op;
01324 if (!grace_obj)
01325 grace_obj = op;
01326
01327
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
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
01358 if (op->stats.sp>op->stats.maxsp*2)
01359 op->stats.sp=op->stats.maxsp*2;
01360
01361
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
01369
01370
01371
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
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
01393 for (i=11;i<=grace_obj->level;i++)
01394 op->stats.maxgrace+=2;
01395 }
01396
01397
01398 if (op->contr->braced) {
01399 ac+=2;
01400 wc+=4;
01401 } else
01402 ac-=dex_bonus[op->stats.Dex];
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
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
01420 if(!(i%6)) wc--;
01421
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 }
01440
01441
01442 if (op->speed>max)
01443 op->speed=max;
01444
01445 if(added_speed>=0)
01446 op->speed+=added_speed/10.0;
01447 else
01448 op->speed /= (float)(1.0-added_speed);
01449
01450 op->speed+=bonus_speed/10.0;
01451
01452 if(op->type == PLAYER) {
01453
01454
01455
01456
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
01464
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
01475
01476
01477
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
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
01497
01498
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
01513
01514
01515
01516
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
01526
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;
01568 int level=0;
01569 int i;
01570
01571
01572 if (!abil || !skin) return;
01573
01574
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
01584
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
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
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;
01625 object *skin = NULL;
01626 object *tmp = NULL;
01627
01628
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
01638 if (abil == NULL) return;
01639
01640
01641
01642
01643 if (who->level > abil->level) {
01644
01645 abil->resist[abil->stats.exp]++;
01646
01647 if (abil->resist[abil->stats.exp]>0 && abil->resist[abil->stats.exp]%5 == 0) {
01648
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
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
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);
01699
01700
01701
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)
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
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);
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);
01774 }
01775
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
01808
01809
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
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
01843 if (op->contr->braced) exp=exp/5;
01844
01845
01846
01847
01848
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
01863
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
01874
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
01978
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
02028 if (!op) {
02029 LOG(llevError,"change_exp() called for null object!\n");
02030 return;
02031 }
02032
02033
02034
02035
02036 if (exp == 0) return;
02037
02038
02039
02040
02041
02042
02043 if (op->type != PLAYER) {
02044
02045 if (!QUERY_FLAG(op, FLAG_ALIVE)) return;
02046
02047
02048
02049
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 {
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;
02077 sint64 level_loss;
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
02086
02087
02088
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
02181 change_exp(op,exp, skill, flag);
02182 }
02183 }