Crossfire Server, Branch 1.12  R12190
living.c
Go to the documentation of this file.
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 }