--- deliantra/server/common/living.C 2006/09/09 21:48:28 1.7 +++ deliantra/server/common/living.C 2007/07/10 05:51:37 1.70 @@ -1,31 +1,26 @@ /* - * static char *rcsid_living_c = - * "$Id: living.C,v 1.7 2006/09/09 21:48:28 root Exp $"; + * This file is part of Crossfire TRT, the Roguelike Realtime MORPG. + * + * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team + * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team + * Copyright (©) 1992,2007 Frank Tore Johansen + * + * Crossfire TRT is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * The authors can be reached via e-mail to */ -/* - CrossFire, A Multiplayer game for X-windows - - Copyright (C) 2002 Mark Wedel & Crossfire Development Team - Copyright (C) 1992 Frank Tore Johansen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - The authors can be reached via e-mail at crossfire-devel@real-time.com -*/ - #include #include @@ -35,23 +30,23 @@ */ #define ADD_EXP(exptotal, exp) {exptotal += exp; if (exptotal > MAX_EXPERIENCE) exptotal = MAX_EXPERIENCE; } -static const int con_bonus[MAX_STAT + 1]={ - -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, - 22,25,30,40,50 +static const int con_bonus[MAX_STAT + 1] = { + -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, + 22, 25, 30, 40, 50 }; /* changed the name of this to "sp_bonus" from "int_bonus" * because Pow can now be the stat that controls spellpoint * advancement. -b.t. */ -static const int sp_bonus[MAX_STAT + 1]={ - -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, - 30,40,50,70,100 +static const int sp_bonus[MAX_STAT + 1] = { + -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, + 30, 40, 50, 70, 100 }; -static const int grace_bonus[MAX_STAT +1] = { - -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, - 30,40,50,70,100 +static const int grace_bonus[MAX_STAT + 1] = { + -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, + 30, 40, 50, 70, 100 }; /* 0.92.7 Changed way charisma works. Values now @@ -71,20 +66,20 @@ */ const float cha_bonus[MAX_STAT + 1] = { 10.0, - 10.0, 9.0, 8.0, 7.0, 6.0, /* 5 */ - 5.0, 4.5, 4.0, 3.5, 3.0, /* 10 */ - 2.9, 2.8, 2.7, 2.6, 2.5, /* 15 */ - 2.4, 2.3, 2.2, 2.1, 2.0, /* 20 */ - 1.9, 1.8, 1.7, 1.6, 1.5, /* 25 */ - 1.4, 1.3, 1.2, 1.1, 1.0 /* 30 */ + 10.0, 9.0, 8.0, 7.0, 6.0, /* 5 */ + 5.0, 4.5, 4.0, 3.5, 3.0, /* 10 */ + 2.9, 2.8, 2.7, 2.6, 2.5, /* 15 */ + 2.4, 2.3, 2.2, 2.1, 2.0, /* 20 */ + 1.9, 1.8, 1.7, 1.6, 1.5, /* 25 */ + 1.4, 1.3, 1.2, 1.1, 1.0 /* 30 */ }; -const int dex_bonus[MAX_STAT + 1]={ - -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 +const int dex_bonus[MAX_STAT + 1] = { + -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 }; /* speed_bonus uses dex as its stat */ -const float speed_bonus[MAX_STAT + 1]={ +const float speed_bonus[MAX_STAT + 1] = { -0.4, -0.35, -0.3, -0.25, -0.2, -0.16, -0.12, -0.09, -0.06, -0.03, -0.01, 0, 0.01, 0.03, 0.05, 0.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3.0 @@ -93,18 +88,18 @@ /* dam_bonus, thaco_bonus, max_carry, weight limit all are based on * strength. */ -const int dam_bonus[MAX_STAT + 1]={ - -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 +const int dam_bonus[MAX_STAT + 1] = { + -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 }; -const int thaco_bonus[MAX_STAT + 1]={ - -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 +const int thaco_bonus[MAX_STAT + 1] = { + -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 }; /* Max you can carry before you start getting extra speed penalties */ -const int max_carry[MAX_STAT + 1]={ - 2,4,7,11,16,22,29,37,46,56,67,79,92,106,121,137,154,172,191,211,232,254,277, - 301,326,352,400,450,500,600,1000 +const int max_carry[MAX_STAT + 1] = { + 2, 4, 7, 11, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137, 154, 172, 191, 211, 232, 254, 277, + 301, 326, 352, 400, 450, 500, 600, 1000 }; /* weight_limit - the absolute most a character can carry - a character can't @@ -114,31 +109,31 @@ * before, you need to start someplace. */ -const uint32 weight_limit[MAX_STAT+ 1] = { - 200000, /* 0 */ - 250000,300000,350000,400000,500000, /* 5*/ - 600000,700000,800000,900000,1000000, /* 10 */ - 1100000,1200000,1300000,1400000,1500000,/* 15 */ - 1650000,1800000,1950000,2100000,2250000,/* 20 */ - 2400000,2550000,2700000,2850000,3000000, /* 25 */ - 3250000,3500000,3750000,4000000,4500000 /*30 */ +const uint32 weight_limit[MAX_STAT + 1] = { + 200000, /* 0 */ + 250000, 300000, 350000, 400000, 500000, /* 5 */ + 600000, 700000, 800000, 900000, 1000000, /* 10 */ + 1100000, 1200000, 1300000, 1400000, 1500000, /* 15 */ + 1650000, 1800000, 1950000, 2100000, 2250000, /* 20 */ + 2400000, 2550000, 2700000, 2850000, 3000000, /* 25 */ + 3250000, 3500000, 3750000, 4000000, 4500000 /*30 */ }; -const int learn_spell[MAX_STAT + 1]={ - 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, - 100,100,100,100,100,100 +const int learn_spell[MAX_STAT + 1] = { + 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, + 100, 100, 100, 100, 100, 100 }; -const int cleric_chance[MAX_STAT + 1]={ - 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 +const int cleric_chance[MAX_STAT + 1] = { + 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 }; -const int turn_bonus[MAX_STAT + 1]={ - -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 +const int turn_bonus[MAX_STAT + 1] = { + -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 }; -const int fear_bonus[MAX_STAT + 1]={ - 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 +const int fear_bonus[MAX_STAT + 1] = { + 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 }; /* @@ -163,36 +158,33 @@ * -b.t. */ -#define MAX_EXP_IN_OBJ levels[settings.max_level]/(MAX_EXP_CAT - 1) +#define MAX_EXP_IN_OBJ levels[settings.max_level]/(MAX_EXP_CAT - 1) -#ifndef WIN32 -extern uint64 *levels; -#else extern sint64 *levels; -#endif #define MAX_SAVE_LEVEL 110 + /* This no longer needs to be changed anytime the number of * levels is increased - rather, did_make_save will do the * right thing and always use range within this table. * for safety, savethrow should not be accessed directly anymore, * and instead did_make_save should be used instead. */ -static const int savethrow[MAX_SAVE_LEVEL+1]={ +static const int savethrow[MAX_SAVE_LEVEL + 1] = { 18, - 18,17,16,15,14,14,13,13,12,12,12,11,11,11,11,10,10,10,10, 9, - 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, - 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + 18, 17, 16, 15, 14, 14, 13, 13, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 9, + 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, + 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; const char *const attacks[NROFATTACKS] = { "physical", "magical", "fire", "electricity", "cold", "confusion", "acid", "drain", "weaponmagic", "ghosthit", "poison", "slow", "paralyze", "turn undead", "fear", "cancellation", "depletion", "death", - "chaos","counterspell","god power","holy power","blinding", "", + "chaos", "counterspell", "god power", "holy power", "blinding", "", "life stealing" }; @@ -201,150 +193,68 @@ "You're feeling clumsy!", "You feel less healthy", "You suddenly begin to lose your memory!", + "Watch out, your mind is going!", + "Your spirit feels drained!", "Your face gets distorted!", - "Watch out, your mind is going!", - "Your spirit feels drained!" }; const char *const restore_msg[NUM_STATS] = { "You feel your strength return.", "You feel your agility return.", "You feel your health return.", + "You feel your memory return.", "You feel your wisdom return.", + "You feel your spirits return.", "You feel your charisma return.", - "You feel your memory return.", - "You feel your spirits return." }; const char *const gain_msg[NUM_STATS] = { - "You feel stronger.", - "You feel more agile.", - "You feel healthy.", - "You feel wiser.", - "You seem to look better.", - "You feel smarter.", - "You feel more potent." + "You feel stronger.", + "You feel more agile.", + "You feel healthy.", + "You feel smarter.", + "You feel wiser.", + "You feel more potent.", + "You seem to look better.", }; const char *const lose_msg[NUM_STATS] = { - "You feel weaker!", - "You feel clumsy!", - "You feel less healthy!", - "You lose some of your memory!", - "You look ugly!", - "You feel stupid!", - "You feel less potent!" + "You feel weaker!", + "You feel clumsy!", + "You feel less healthy!", + "You feel stupid!", + "You lose some of your memory!", + "You feel less potent!", + "You look ugly!", }; const char *const statname[NUM_STATS] = { - "strength", "dexterity", "constitution", "wisdom", "charisma", "intelligence","power" + "strength", "dexterity", "constitution", "intelligence", "wisdom", "power", "charisma" }; const char *const short_stat_name[NUM_STATS] = { - "Str", "Dex", "Con", "Wis", "Cha", "Int","Pow" + "Str", "Dex", "Con", "Int", "Wis", "Pow", "Cha" }; /* - * sets Str/Dex/con/Wis/Cha/Int/Pow in stats to value, depending on - * what attr is (STR to POW). - */ - -void -set_attr_value(living *stats,int attr,sint8 value) { - switch(attr) { - case STR: - stats->Str=value; - break; - case DEX: - stats->Dex=value; - break; - case CON: - stats->Con=value; - break; - case WIS: - stats->Wis=value; - break; - case POW: - stats->Pow=value; - break; - case CHA: - stats->Cha=value; - break; - case INT: - stats->Int=value; - break; - } -} - -/* * Like set_attr_value(), but instead the value (which can be negative) * is added to the specified stat. */ - void -change_attr_value(living *stats,int attr,sint8 value) { - if (value==0) return; - switch(attr) { - case STR: - stats->Str+=value; - break; - case DEX: - stats->Dex+=value; - break; - case CON: - stats->Con+=value; - break; - case WIS: - stats->Wis+=value; - break; - case POW: - stats->Pow+=value; - break; - case CHA: - stats->Cha+=value; - break; - case INT: - stats->Int+=value; - break; - default: - LOG(llevError,"Invalid attribute in change_attr_value: %d\n", attr); - } -} - -/* - * returns the specified stat. See also set_attr_value(). - */ - -sint8 -get_attr_value(const living *stats,int attr) { - switch(attr) { - case STR: - return(stats->Str); - case DEX: - return(stats->Dex); - case CON: - return(stats->Con); - case WIS: - return(stats->Wis); - case CHA: - return(stats->Cha); - case INT: - return(stats->Int); - case POW: - return(stats->Pow); - } - return 0; +change_attr_value (living *stats, int attr, sint8 value) +{ + stats->stat (attr) += value; } /* * Ensures that all stats (str/dex/con/wis/cha/int) are within the * 1-30 stat limit. */ - -void check_stat_bounds(living *stats) { - int i,v; - for(i=0;iMAX_STAT) - set_attr_value(stats,i,MAX_STAT); - else if(vstat (i); + v = clamp (v, MIN_STAT, MAX_STAT); + } } #define ORIG_S(xyz,abc) (op->contr->orig_stats.abc) @@ -357,397 +267,443 @@ new_draw_info(NDI_UNIQUE, 0, op, (flag>0)?msg1:msg2); /* return 1 if we sucessfully changed a stat, 0 if nothing was changed. */ + /* flag is set to 1 if we are applying the object, -1 if we are removing * the object. * It is the calling functions responsibilty to check to see if the object * can be applied or not. * The main purpose of calling this function is the messages that are - * displayed - fix_player should really always be called after this when + * displayed - update_stats should really always be called after this when * removing an object - that is because it is impossible to know if some object * is the only source of an attacktype or spell attunement, so this function * will clear the bits, but the player may still have some other object * that gives them that ability. */ -int change_abil(object *op, object *tmp) { - int flag=QUERY_FLAG(tmp,FLAG_APPLIED)?1:-1,i,j,success=0; - char message[MAX_BUF]; - int potion_max=0; - - /* remember what object was like before it was changed. note that - * refop is a local copy of op only to be used for detecting changes - * found by fix_player. refop is not a real object - */ - object_pod refop; - memcpy (&refop, static_cast(op), sizeof (object_pod)); - - if(op->type==PLAYER) { - if (tmp->type==POTION) { - potion_max=1; - for(j=0;jcontr->orig_stats),j); - i = get_attr_value(&(tmp->stats),j); - - /* nstat is what the stat will be after use of the potion */ - nstat = flag*i + ostat; - - /* Do some bounds checking. While I don't think any - * potions do so right now, there is the potential for potions - * that adjust that stat by more than one point, so we need - * to allow for that. - */ - if (nstat < 1 && i*flag < 0 ) nstat = 1; - else if (nstat > 20 + get_attr_value(&(op->arch->clone.stats),j)) { - nstat = 20 + get_attr_value(&(op->arch->clone.stats),j); - } - if (nstat != ostat) { - set_attr_value(&(op->contr->orig_stats), j, nstat); - potion_max=0; +int +change_abil (object *op, object *tmp) +{ + int flag = QUERY_FLAG (tmp, FLAG_APPLIED) ? 1 : -1, success = 0; + char message[MAX_BUF]; + int potion_max = 0; + + /* remember what object was like before it was changed. note that + * refop is a local copy of op only to be used for detecting changes + * found by update_stats. refop is not a real object + */ + object_copy refop = *op; + + if (op->type == PLAYER) + { + if (tmp->type == POTION) + { + potion_max = 1; + for (int j = 0; j < NUM_STATS; j++) + { + int ostat = op->contr->orig_stats.stat (j); + int i = tmp->stats.stat (j); + + /* nstat is what the stat will be after use of the potion */ + int nstat = flag * i + ostat; + + /* Do some bounds checking. While I don't think any + * potions do so right now, there is the potential for potions + * that adjust that stat by more than one point, so we need + * to allow for that. + */ + if (nstat < 1 && i * flag < 0) + nstat = 1; + else if (nstat > 20 + op->arch->stats.stat (j)) + nstat = 20 + op->arch->stats.stat (j); + + if (nstat != ostat) + { + op->contr->orig_stats.stat (j) = nstat; + potion_max = 0; } - else if (i) { - /* potion is useless - player has already hit the natural maximum */ - potion_max = 1; + else if (i) + { + /* potion is useless - player has already hit the natural maximum */ + potion_max = 1; } } - /* This section of code ups the characters normal stats also. I am not - * sure if this is strictly necessary, being that fix_player probably - * recalculates this anyway. - */ - for(j=0;jstats),j,flag*get_attr_value(&(tmp->stats),j)); - check_stat_bounds(&(op->stats)); - } /* end of potion handling code */ - } - - /* reset attributes that fix_player doesn't reset since it doesn't search - * everything to set - */ - if(flag == -1) { - op->attacktype&=~tmp->attacktype; - op->path_attuned&=~tmp->path_attuned; - op->path_repelled&=~tmp->path_repelled; - op->path_denied&=~tmp->path_denied; - /* Presuming here that creatures only have move_type, - * and not the other move_ fields. - */ - op->move_type &= ~tmp->move_type; + + /* This section of code ups the characters normal stats also. I am not + * sure if this is strictly necessary, being that fix_player probably + * recalculates this anyway. + */ + for (int j = 0; j < NUM_STATS; j++) + change_attr_value (&op->stats, j, flag * tmp->stats.stat (j)); + + check_stat_bounds (&op->stats); + } /* end of potion handling code */ } - /* call fix_player since op object could have whatever attribute due - * to multiple items. if fix_player always has to be called after - * change_ability then might as well call it from here - */ - fix_player(op); - - /* Fix player won't add the bows ability to the player, so don't - * print out message if this is a bow. - */ - if(tmp->attacktype & AT_CONFUSION && tmp->type != BOW) { - success=1; - DIFF_MSG(flag, "Your hands begin to glow red.", - "Your hands stop glowing red."); - } - if ( QUERY_FLAG(op,FLAG_LIFESAVE) != QUERY_FLAG(&refop,FLAG_LIFESAVE)){ - success=1; - DIFF_MSG(flag, "You feel very protected.", - "You don't feel protected anymore."); - } - if ( QUERY_FLAG(op,FLAG_REFL_MISSILE) != QUERY_FLAG(&refop,FLAG_REFL_MISSILE)){ - success=1; - DIFF_MSG(flag, "A magic force shimmers around you.", - "The magic force fades away."); - } - if ( QUERY_FLAG(op,FLAG_REFL_SPELL) != QUERY_FLAG(&refop,FLAG_REFL_SPELL)){ - success=1; - DIFF_MSG(flag, "You feel more safe now, somehow.", - "Suddenly you feel less safe, somehow."); - } - /* movement type has changed. We don't care about cases where - * user has multiple items giving the same type appled like we - * used to - that is more work than what we gain, plus messages - * can be misleading (a little higher could be miscontrued from - * from fly high) - */ - if (tmp->move_type && op->move_type != refop.move_type) { - success=1; + /* reset attributes that update_stats doesn't reset since it doesn't search + * everything to set + */ + if (flag == -1) + { + op->attacktype &= ~tmp->attacktype; + op->path_attuned &= ~tmp->path_attuned; + op->path_repelled &= ~tmp->path_repelled; + op->path_denied &= ~tmp->path_denied; + /* Presuming here that creatures only have move_type, + * and not the other move_ fields. + */ + op->move_type &= ~tmp->move_type; + } - /* MOVE_FLY_HIGH trumps MOVE_FLY_LOW - changing your move_fly_low - * status doesn't make a difference if you are flying high - */ - if (tmp->move_type & MOVE_FLY_LOW && !(op->move_type & MOVE_FLY_HIGH)) { - DIFF_MSG(flag, "You start to float in the air!.", "You float down to the ground."); + /* call fix_player since op object could have whatever attribute due + * to multiple items. if update_stats always has to be called after + * change_ability then might as well call it from here + */ + op->update_stats (); + + /* update_stats won't add the bows ability to the player, so don't + * print out message if this is a bow. + */ + if (tmp->attacktype & AT_CONFUSION && tmp->type != BOW) + { + success = 1; + DIFF_MSG (flag, "Your hands begin to glow red.", "Your hands stop glowing red."); + } + + if (QUERY_FLAG (op, FLAG_LIFESAVE) != QUERY_FLAG (&refop, FLAG_LIFESAVE)) + { + success = 1; + DIFF_MSG (flag, "You feel very protected.", "You don't feel protected anymore."); + } + + if (QUERY_FLAG (op, FLAG_REFL_MISSILE) != QUERY_FLAG (&refop, FLAG_REFL_MISSILE)) + { + success = 1; + DIFF_MSG (flag, "A magic force shimmers around you.", "The magic force fades away."); + } + + if (QUERY_FLAG (op, FLAG_REFL_SPELL) != QUERY_FLAG (&refop, FLAG_REFL_SPELL)) + { + success = 1; + DIFF_MSG (flag, "You feel more safe now, somehow.", "Suddenly you feel less safe, somehow."); + } + + /* movement type has changed. We don't care about cases where + * user has multiple items giving the same type appled like we + * used to - that is more work than what we gain, plus messages + * can be misleading (a little higher could be miscontrued from + * from fly high) + */ + if (tmp->move_type && op->move_type != refop.move_type) + { + success = 1; + + /* MOVE_FLY_HIGH trumps MOVE_FLY_LOW - changing your move_fly_low + * status doesn't make a difference if you are flying high + */ + if (tmp->move_type & MOVE_FLY_LOW && !(op->move_type & MOVE_FLY_HIGH)) + { + DIFF_MSG (flag, "You start to float in the air!.", "You float down to the ground."); } - if (tmp->move_type & MOVE_FLY_HIGH) { - /* double conditional - second case covers if you have move_fly_low - - * in that case, you don't actually land - */ - DIFF_MSG(flag, "You soar into the air air!.", - (op->move_type&MOVE_FLY_LOW ? "You fly lower in the air": - "You float down to the ground.")); + if (tmp->move_type & MOVE_FLY_HIGH) + { + /* double conditional - second case covers if you have move_fly_low - + * in that case, you don't actually land + */ + DIFF_MSG (flag, "You soar into the air!", + (op->move_type & MOVE_FLY_LOW ? "You fly lower in the air" : "You float down to the ground.")); } - if (tmp->move_type & MOVE_SWIM) - DIFF_MSG(flag,"You feel ready for a swim", "You no longer feel like swimming"); - /* Changing move status may mean you are affected by things you weren't before */ - check_move_on(op, op); + if (tmp->move_type & MOVE_SWIM) + DIFF_MSG (flag, "You feel ready for a swim", "You no longer feel like swimming"); + + /* Changing move status may mean you are affected by things you weren't before */ + check_move_on (op, op); } - /* becoming UNDEAD... a special treatment for this flag. Only those not - * originally undead may change their status - */ - if(!QUERY_FLAG(&op->arch->clone,FLAG_UNDEAD)) - if ( QUERY_FLAG(op,FLAG_UNDEAD) != QUERY_FLAG(&refop,FLAG_UNDEAD)) { - success=1; - if(flag>0) { - op->race = "undead"; - new_draw_info(NDI_UNIQUE, 0, op,"Your lifeforce drains away!"); - } else { - op->race = op->arch->clone.race; - new_draw_info(NDI_UNIQUE, 0, op,"Your lifeforce returns!"); - } - } + /* becoming UNDEAD... a special treatment for this flag. Only those not + * originally undead may change their status + */ + if (!QUERY_FLAG (op->arch, FLAG_UNDEAD)) + if (QUERY_FLAG (op, FLAG_UNDEAD) != QUERY_FLAG (&refop, FLAG_UNDEAD)) + { + success = 1; + if (flag > 0) + { + op->race = "undead"; + new_draw_info (NDI_UNIQUE, 0, op, "Your lifeforce drains away!"); + } + else + { + op->race = op->arch->race; + new_draw_info (NDI_UNIQUE, 0, op, "Your lifeforce returns!"); + } + } - if ( QUERY_FLAG(op,FLAG_STEALTH) != QUERY_FLAG(&refop,FLAG_STEALTH)){ - success=1; - DIFF_MSG(flag, "You walk more quietly.", "You walk more noisily."); + if (QUERY_FLAG (op, FLAG_STEALTH) != QUERY_FLAG (&refop, FLAG_STEALTH)) + { + success = 1; + DIFF_MSG (flag, "You walk more quietly.", "You walk more noisily."); } - if ( QUERY_FLAG(op,FLAG_MAKE_INVIS) != QUERY_FLAG(&refop,FLAG_MAKE_INVIS)){ - success=1; - DIFF_MSG(flag, "You become transparent.", "You can see yourself."); + + if (QUERY_FLAG (op, FLAG_MAKE_INVIS) != QUERY_FLAG (&refop, FLAG_MAKE_INVIS)) + { + success = 1; + DIFF_MSG (flag, "You become transparent.", "You can see yourself."); } - /* blinded you can tell if more blinded since blinded player has minimal - * vision - */ - if(QUERY_FLAG(tmp,FLAG_BLIND)) { - success=1; - if(flag>0) { - if(QUERY_FLAG(op,FLAG_WIZ)) - new_draw_info(NDI_UNIQUE, 0, op,"Your mortal self is blinded."); - else { - new_draw_info(NDI_UNIQUE, 0, op,"You are blinded."); - SET_FLAG(op,FLAG_BLIND); - if(op->type==PLAYER) - op->contr->do_los=1; + + /* blinded you can tell if more blinded since blinded player has minimal + * vision + */ + if (QUERY_FLAG (tmp, FLAG_BLIND)) + { + success = 1; + if (flag > 0) + { + if (QUERY_FLAG (op, FLAG_WIZ)) + new_draw_info (NDI_UNIQUE, 0, op, "Your mortal self is blinded."); + else + { + new_draw_info (NDI_UNIQUE, 0, op, "You are blinded."); + SET_FLAG (op, FLAG_BLIND); + if (op->type == PLAYER) + op->contr->do_los = 1; } - } else { - if(QUERY_FLAG(op,FLAG_WIZ)) - new_draw_info(NDI_UNIQUE, 0, op,"Your mortal self can now see again."); - else { - new_draw_info(NDI_UNIQUE, 0, op,"Your vision returns."); - CLEAR_FLAG(op,FLAG_BLIND); - if(op->type==PLAYER) - op->contr->do_los=1; + } + else + { + if (QUERY_FLAG (op, FLAG_WIZ)) + new_draw_info (NDI_UNIQUE, 0, op, "Your mortal self can now see again."); + else + { + new_draw_info (NDI_UNIQUE, 0, op, "Your vision returns."); + CLEAR_FLAG (op, FLAG_BLIND); + if (op->type == PLAYER) + op->contr->do_los = 1; } } } - if ( QUERY_FLAG(op,FLAG_SEE_IN_DARK) != QUERY_FLAG(&refop,FLAG_SEE_IN_DARK)){ - success=1; - if(op->type==PLAYER) - op->contr->do_los=1; - DIFF_MSG(flag, "Your vision is better in the dark.", "You see less well in the dark."); + if (QUERY_FLAG (op, FLAG_SEE_IN_DARK) != QUERY_FLAG (&refop, FLAG_SEE_IN_DARK)) + { + success = 1; + if (op->type == PLAYER) + op->contr->do_los = 1; + DIFF_MSG (flag, "Your vision is better in the dark.", "You see less well in the dark."); } - if ( QUERY_FLAG(op,FLAG_XRAYS) != QUERY_FLAG(&refop,FLAG_XRAYS)){ - success=1; - if(flag>0) { - if(QUERY_FLAG(op,FLAG_WIZ)) - new_draw_info(NDI_UNIQUE, 0, op,"Your vision becomes a little clearer."); - else { - new_draw_info(NDI_UNIQUE, 0, op,"Everything becomes transparent."); - if(op->type==PLAYER) - op->contr->do_los=1; + if (QUERY_FLAG (op, FLAG_XRAYS) != QUERY_FLAG (&refop, FLAG_XRAYS)) + { + success = 1; + if (flag > 0) + { + if (QUERY_FLAG (op, FLAG_WIZ)) + new_draw_info (NDI_UNIQUE, 0, op, "Your vision becomes a little clearer."); + else + { + new_draw_info (NDI_UNIQUE, 0, op, "Everything becomes transparent."); + if (op->type == PLAYER) + op->contr->do_los = 1; } - } else { - if(QUERY_FLAG(op,FLAG_WIZ)) - new_draw_info(NDI_UNIQUE, 0, op,"Your vision becomes a bit out of focus."); - else { - new_draw_info(NDI_UNIQUE, 0, op,"Everything suddenly looks very solid."); - if(op->type==PLAYER) - op->contr->do_los=1; + } + else + { + if (QUERY_FLAG (op, FLAG_WIZ)) + new_draw_info (NDI_UNIQUE, 0, op, "Your vision becomes a bit out of focus."); + else + { + new_draw_info (NDI_UNIQUE, 0, op, "Everything suddenly looks very solid."); + if (op->type == PLAYER) + op->contr->do_los = 1; } } } - if(tmp->stats.luck) { - success=1; - DIFF_MSG(flag*tmp->stats.luck, "You feel more lucky.", "You feel less lucky."); + if (tmp->stats.luck) + { + success = 1; + DIFF_MSG (flag * tmp->stats.luck, "You feel more lucky.", "You feel less lucky."); } - if(tmp->stats.hp && op->type==PLAYER) { - success=1; - DIFF_MSG(flag*tmp->stats.hp, "You feel much more healthy!", - "You feel much less healthy!"); + if (tmp->stats.hp && op->type == PLAYER) + { + success = 1; + DIFF_MSG (flag * tmp->stats.hp, "You feel much more healthy!", "You feel much less healthy!"); } - if(tmp->stats.sp && op->type==PLAYER && tmp->type!=SKILL) { - success=1; - DIFF_MSG(flag*tmp->stats.sp, "You feel one with the powers of magic!", - "You suddenly feel very mundane."); + if (tmp->stats.sp && op->type == PLAYER && tmp->type != SKILL) + { + success = 1; + DIFF_MSG (flag * tmp->stats.sp, "You feel one with the powers of magic!", "You suddenly feel very mundane."); } - /* for the future when artifacts set this -b.t. */ - if(tmp->stats.grace && op->type==PLAYER) { - success=1; - DIFF_MSG(flag*tmp->stats.grace, "You feel closer to your god!", - "You suddenly feel less holy."); + /* for the future when artifacts set this -b.t. */ + if (tmp->stats.grace && op->type == PLAYER) + { + success = 1; + DIFF_MSG (flag * tmp->stats.grace, "You feel closer to your god!", "You suddenly feel less holy."); } - if(tmp->stats.food && op->type==PLAYER) { - success=1; - DIFF_MSG(flag*tmp->stats.food, "You feel your digestion slowing down.", - "You feel your digestion speeding up."); + if (tmp->stats.food && op->type == PLAYER) + { + success = 1; + DIFF_MSG (flag * tmp->stats.food, "You feel your digestion slowing down.", "You feel your digestion speeding up."); } - /* Messages for changed resistance */ - for (i=0; iresist[i] != refop.resist[i]) { - success=1; - if (op->resist[i] > refop.resist[i]) - sprintf(message, "Your resistance to %s rises to %d%%.", - change_resist_msg[i], op->resist[i]); - else - sprintf(message, "Your resistance to %s drops to %d%%.", - change_resist_msg[i], op->resist[i]); - - new_draw_info(NDI_UNIQUE|NDI_BLUE, 0, op, message); + /* Messages for changed resistance */ + for (int i = 0; i < NROFATTACKS; i++) + { + if (i == ATNR_PHYSICAL) + continue; /* Don't display about armour */ + + if (op->resist[i] != refop.resist[i]) + { + success = 1; + if (op->resist[i] > refop.resist[i]) + sprintf (message, "Your resistance to %s rises to %d%%.", change_resist_msg[i], op->resist[i]); + else + sprintf (message, "Your resistance to %s drops to %d%%.", change_resist_msg[i], op->resist[i]); + + new_draw_info (NDI_UNIQUE | NDI_BLUE, 0, op, message); } } - if(tmp->type!=EXPERIENCE && !potion_max) { - for (j=0; jstats),j))!=0) { - success=1; - DIFF_MSG(i * flag, gain_msg[j], lose_msg[j]); + if (!potion_max) + { + for (int j = 0; j < NUM_STATS; j++) + { + if (int i = tmp->stats.stat (j)) + { + success = 1; + DIFF_MSG (i * flag, gain_msg[j], lose_msg[j]); } } } - return success; + + return success; } /* * Stat draining by Vick 930307 * (Feeling evil, I made it work as well now. -Frank 8) */ - -void drain_stat(object *op) { - drain_specific_stat(op, RANDOM()%NUM_STATS); +void +object::drain_stat () +{ + drain_specific_stat (rndm (NUM_STATS)); } -void drain_specific_stat(object *op, int deplete_stats) { +void +object::drain_specific_stat (int deplete_stats) +{ object *tmp; archetype *at; - at = find_archetype(ARCH_DEPLETION); - if (!at) { - LOG(llevError, "Couldn't find archetype depletion.\n"); - return; - } else { - tmp = present_arch_in_ob(at, op); - if (!tmp) { - tmp = arch_to_object(at); - tmp = insert_ob_in_ob(tmp, op); - SET_FLAG(tmp,FLAG_APPLIED); - } - } - - new_draw_info(NDI_UNIQUE, 0, op, drain_msg[deplete_stats]); - change_attr_value(&tmp->stats, deplete_stats, -1); - fix_player(op); + at = archetype::find (ARCH_DEPLETION); + if (!at) + { + LOG (llevError, "Couldn't find archetype depletion.\n"); + return; + } + else + { + tmp = present_arch_in_ob (at, this); + + if (!tmp) + { + tmp = arch_to_object (at); + tmp = insert_ob_in_ob (tmp, this); + SET_FLAG (tmp, FLAG_APPLIED); + } + } + + new_draw_info (NDI_UNIQUE, 0, this, drain_msg[deplete_stats]); + change_attr_value (&tmp->stats, deplete_stats, -1); + update_stats (); } /* * A value of 0 indicates timeout, otherwise change the luck of the object. * via an applied bad_luck object. */ +void +object::change_luck (int value) +{ + archetype *at = archetype::find ("luck"); + if (!at) + LOG (llevError, "Couldn't find archetype luck.\n"); + else + { + object *tmp = present_arch_in_ob (at, this); -void change_luck(object *op, int value) { - object *tmp; - archetype *at; - int new_luck; + if (!tmp) + { + if (!value) + return; - at = find_archetype("luck"); - if (!at) - LOG(llevError, "Couldn't find archetype luck.\n"); - else { - tmp = present_arch_in_ob(at, op); - if (!tmp) { - if (!value) - return; - tmp = arch_to_object(at); - tmp = insert_ob_in_ob(tmp, op); - SET_FLAG(tmp,FLAG_APPLIED); - } - if (value) { - /* Limit the luck value of the bad luck object to +/-100. This - * (arbitrary) value prevents overflows (both in the bad luck object and - * in op itself). - */ - new_luck = tmp->stats.luck+value; - if (new_luck >= -100 && new_luck <= 100) { - op->stats.luck+=value; - tmp->stats.luck = new_luck; - } - } else { - if (!tmp->stats.luck) { - return; - } - /* Randomly change the players luck. Basically, we move it - * back neutral (if greater>0, subtract, otherwise add) - */ - if (RANDOM()%(FABS(tmp->stats.luck)) >= RANDOM()%30) { - int diff = tmp->stats.luck>0?-1:1; - op->stats.luck += diff; - tmp->stats.luck += diff; - } + tmp = arch_to_object (at); + tmp = insert_ob_in_ob (tmp, this); + SET_FLAG (tmp, FLAG_APPLIED); + } + + if (value) + { + /* Limit the luck value of the bad luck object to +/-100. This + * (arbitrary) value prevents overflows (both in the bad luck object and + * in op itself). + */ + int new_luck = tmp->stats.luck + value; + + if (new_luck >= -100 && new_luck <= 100) + { + stats.luck += value; + tmp->stats.luck = new_luck; + } + } + else + { + if (!tmp->stats.luck) + return; + + /* Randomly change the players luck. Basically, we move it + * back neutral (if greater>0, subtract, otherwise add) + */ + if (rndm (abs (tmp->stats.luck)) >= rndm (30)) + { + int diff = tmp->stats.luck > 0 ? -1 : 1; + + stats.luck += diff; + tmp->stats.luck += diff; + } + } } - } } /* * Subtracts stat-bonuses given by the class which the player has chosen. */ - -void remove_statbonus(object *op) { - op->stats.Str -= op->arch->clone.stats.Str; - op->stats.Dex -= op->arch->clone.stats.Dex; - op->stats.Con -= op->arch->clone.stats.Con; - op->stats.Wis -= op->arch->clone.stats.Wis; - op->stats.Pow -= op->arch->clone.stats.Pow; - op->stats.Cha -= op->arch->clone.stats.Cha; - op->stats.Int -= op->arch->clone.stats.Int; - op->contr->orig_stats.Str -= op->arch->clone.stats.Str; - op->contr->orig_stats.Dex -= op->arch->clone.stats.Dex; - op->contr->orig_stats.Con -= op->arch->clone.stats.Con; - op->contr->orig_stats.Wis -= op->arch->clone.stats.Wis; - op->contr->orig_stats.Pow -= op->arch->clone.stats.Pow; - op->contr->orig_stats.Cha -= op->arch->clone.stats.Cha; - op->contr->orig_stats.Int -= op->arch->clone.stats.Int; +void +object::remove_statbonus () +{ + for (int i = 0; i < NUM_STATS; ++i) + { + sint8 v = arch->stats.stat (i); + stats.stat (i) -= v; + contr->orig_stats.stat (i) -= v; + } } /* * Adds stat-bonuses given by the class which the player has chosen. */ - -void add_statbonus(object *op) { - op->stats.Str += op->arch->clone.stats.Str; - op->stats.Dex += op->arch->clone.stats.Dex; - op->stats.Con += op->arch->clone.stats.Con; - op->stats.Wis += op->arch->clone.stats.Wis; - op->stats.Pow += op->arch->clone.stats.Pow; - op->stats.Cha += op->arch->clone.stats.Cha; - op->stats.Int += op->arch->clone.stats.Int; - op->contr->orig_stats.Str += op->arch->clone.stats.Str; - op->contr->orig_stats.Dex += op->arch->clone.stats.Dex; - op->contr->orig_stats.Con += op->arch->clone.stats.Con; - op->contr->orig_stats.Wis += op->arch->clone.stats.Wis; - op->contr->orig_stats.Pow += op->arch->clone.stats.Pow; - op->contr->orig_stats.Cha += op->arch->clone.stats.Cha; - op->contr->orig_stats.Int += op->arch->clone.stats.Int; +void +object::add_statbonus () +{ + for (int i = 0; i < NUM_STATS; ++i) + { + sint8 v = arch->stats.stat (i); + stats.stat (i) += v; + contr->orig_stats.stat (i) += v; + } } /* @@ -756,596 +712,706 @@ * and players; the "player" in the name is purely an archaic inheritance. * This functions starts from base values (archetype or player object) * and then adjusts them according to what the player has equipped. + * + * July 95 - inserted stuff to handle new skills/exp system - b.t. + * spell system split, grace points now added to system --peterm */ -/* July 95 - inserted stuff to handle new skills/exp system - b.t. - spell system split, grace points now added to system --peterm - */ +void +object::update_stats () +{ + int i, j; + float f, max = 9, added_speed = 0, bonus_speed = 0, sp_tmp, speed_reduce_from_disease = 1; + int weapon_weight = 0, weapon_speed = 0; + int best_wc = 0, best_ac = 0, wc = 0, ac = 0; + int prot[NROFATTACKS], vuln[NROFATTACKS], potion_resist[NROFATTACKS]; + object *grace_obj = NULL, *mana_obj = NULL, *tmp; + float old_speed = speed; -void fix_player(object *op) { - int i,j; - float f,max=9,added_speed=0,bonus_speed=0, sp_tmp,speed_reduce_from_disease=1; - int weapon_weight=0,weapon_speed=0; - int best_wc=0, best_ac=0, wc=0, ac=0; - int prot[NROFATTACKS], vuln[NROFATTACKS], potion_resist[NROFATTACKS]; - object *grace_obj=NULL,*mana_obj=NULL,*wc_obj=NULL,*tmp; - - /* First task is to clear all the values back to their original values */ - if(op->type==PLAYER) { - for(i=0;istats),i,get_attr_value(&(op->contr->orig_stats),i)); - } - if (settings.spell_encumbrance == TRUE) - op->contr->encumbrance=0; - - op->attacktype=0; - op->contr->digestion = 0; - op->contr->gen_hp = 0; - op->contr->gen_sp = 0; - op->contr->gen_grace = 0; - op->contr->gen_sp_armour = 10; - op->contr->item_power = 0; - - /* Don't clobber all the range_ values. range_golem otherwise - * gets reset for no good reason, and we don't want to reset - * range_magic (what spell is readied). These three below - * well get filled in based on what the player has equipped. - */ - op->contr->ranges[range_bow] = NULL; - op->contr->ranges[range_misc] = NULL; - op->contr->ranges[range_skill] = NULL; - } - memcpy(op->body_used, op->body_info, sizeof(op->body_info)); - - op->slaying = 0; - - if(!QUERY_FLAG(op,FLAG_WIZ)) { - CLEAR_FLAG(op, FLAG_XRAYS); - CLEAR_FLAG(op, FLAG_MAKE_INVIS); - } - - CLEAR_FLAG(op,FLAG_LIFESAVE); - CLEAR_FLAG(op,FLAG_STEALTH); - CLEAR_FLAG(op,FLAG_BLIND); - if ( ! QUERY_FLAG (&op->arch->clone, FLAG_REFL_SPELL)) - CLEAR_FLAG(op,FLAG_REFL_SPELL); - if ( ! QUERY_FLAG (&op->arch->clone, FLAG_REFL_MISSILE)) - CLEAR_FLAG(op,FLAG_REFL_MISSILE); - if(!QUERY_FLAG(&op->arch->clone,FLAG_UNDEAD)) - CLEAR_FLAG(op,FLAG_UNDEAD); - if ( ! QUERY_FLAG (&op->arch->clone, FLAG_SEE_IN_DARK)) - CLEAR_FLAG(op,FLAG_SEE_IN_DARK); - - op->path_attuned=op->arch->clone.path_attuned; - op->path_repelled=op->arch->clone.path_repelled; - op->path_denied=op->arch->clone.path_denied; - op->glow_radius=op->arch->clone.glow_radius; - op->move_type = op->arch->clone.move_type; - op->chosen_skill = NULL; - - /* initializing resistances from the values in player/monster's - * archetype clone - */ - memcpy(&op->resist, &op->arch->clone.resist, sizeof(op->resist)); - - for (i=0;iresist[i] > 0) - prot[i]= op->resist[i], vuln[i]=0; - else - vuln[i]= -(op->resist[i]), prot[i]=0; - potion_resist[i]=0; + /* First task is to clear all the values back to their original values */ + if (type == PLAYER) + { + for (i = 0; i < NUM_STATS; i++) + stats.stat (i) = contr->orig_stats.stat (i); + + if (settings.spell_encumbrance == TRUE) + contr->encumbrance = 0; + + attacktype = 0; + + contr->digestion = 0; + contr->gen_hp = 0; + contr->gen_sp = 0; + contr->gen_grace = 0; + contr->gen_sp_armour = 10; + contr->item_power = 0; } - - wc=op->arch->clone.stats.wc; - op->stats.dam=op->arch->clone.stats.dam; - - /* for players which cannot use armour, they gain AC -1 per 3 levels, - * plus a small amount of physical resist, those poor suckers. ;) - * the fact that maxlevel is factored in could be considered sort of bogus - - * we should probably give them some bonus and cap it off - otherwise, - * basically, if a server updates its max level, these playes may find - * that their protection from physical goes down - */ - if(!QUERY_FLAG(op,FLAG_USE_ARMOUR) && op->type==PLAYER) { - ac=MAX(-10,op->arch->clone.stats.ac - op->level/3); - prot[ATNR_PHYSICAL] += ((100-prot[AT_PHYSICAL])*(80*op->level/settings.max_level))/100; - } - else - ac=op->arch->clone.stats.ac; - - op->stats.luck=op->arch->clone.stats.luck; - op->speed = op->arch->clone.speed; - - /* OK - we've reset most all the objects attributes to sane values. - * now go through and make adjustments for what the player has equipped. - */ - - for(tmp=op->inv;tmp!=NULL;tmp=tmp->below) { - /* See note in map.c:update_position about making this additive - * since light sources are never applied, need to put check here. - */ - if (tmp->glow_radius > op->glow_radius) op->glow_radius=tmp->glow_radius; - /* This happens because apply_potion calls change_abil with the potion - * applied so we can tell the player what chagned. But change_abil - * then calls this function. - */ - if (QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type == POTION) { - continue; - } + for (int i = NUM_BODY_LOCATIONS; i--; ) + slot[i].used = slot[i].info; + + slaying = 0; + + if (!QUERY_FLAG (this, FLAG_WIZ)) + { + CLEAR_FLAG (this, FLAG_XRAYS); + CLEAR_FLAG (this, FLAG_MAKE_INVIS); + } + + CLEAR_FLAG (this, FLAG_LIFESAVE); + CLEAR_FLAG (this, FLAG_STEALTH); + CLEAR_FLAG (this, FLAG_BLIND); - /* For some things, we don't care what is equipped */ - if (tmp->type == SKILL) { - /* Want to take the highest skill here. */ - if (IS_MANA_SKILL(tmp->subtype)) { - if (!mana_obj) mana_obj=tmp; - else if (tmp->level > mana_obj->level) mana_obj = tmp; - } - if (IS_GRACE_SKILL(tmp->subtype)) { - if (!grace_obj) grace_obj=tmp; - else if (tmp->level > grace_obj->level) grace_obj = tmp; + if (!QUERY_FLAG (arch, FLAG_REFL_SPELL )) CLEAR_FLAG (this, FLAG_REFL_SPELL); + if (!QUERY_FLAG (arch, FLAG_REFL_MISSILE)) CLEAR_FLAG (this, FLAG_REFL_MISSILE); + if (!QUERY_FLAG (arch, FLAG_UNDEAD )) CLEAR_FLAG (this, FLAG_UNDEAD); + if (!QUERY_FLAG (arch, FLAG_SEE_IN_DARK )) CLEAR_FLAG (this, FLAG_SEE_IN_DARK); + + path_attuned = arch->path_attuned; + path_repelled = arch->path_repelled; + path_denied = arch->path_denied; + glow_radius = arch->glow_radius; + move_type = arch->move_type; + + chosen_skill = 0; + + /* initializing resistances from the values in player/monster's + * archetype clone + */ + memcpy (&resist, &arch->resist, sizeof (resist)); + + for (i = 0; i < NROFATTACKS; i++) + { + if (resist[i] > 0) + prot[i] = resist[i], vuln[i] = 0; + else + vuln[i] = -(resist[i]), prot[i] = 0; + + potion_resist[i] = 0; + } + + wc = arch->stats.wc; + stats.dam = arch->stats.dam; + + /* for players which cannot use armour, they gain AC -1 per 3 levels, + * plus a small amount of physical resist, those poor suckers. ;) + * the fact that maxlevel is factored in could be considered sort of bogus - + * we should probably give them some bonus and cap it off - otherwise, + * basically, if a server updates its max level, these playes may find + * that their protection from physical goes down + */ + if (!QUERY_FLAG (this, FLAG_USE_ARMOUR) && type == PLAYER) + { + ac = MAX (-10, arch->stats.ac - level / 3); + prot[ATNR_PHYSICAL] += ((100 - prot[AT_PHYSICAL]) * (80 * level / settings.max_level)) / 100; + } + else + ac = arch->stats.ac; + + stats.luck = arch->stats.luck; + speed = arch->speed; + + /* OK - we've reset most all the objects attributes to sane values. + * now go through and make adjustments for what the player has equipped. + */ + for (tmp = inv; tmp; tmp = tmp->below) + { + /* This happens because apply_potion calls change_abil with the potion + * applied so we can tell the player what changed. But change_abil + * then calls this function. + */ + if (QUERY_FLAG (tmp, FLAG_APPLIED) && tmp->type == POTION) + continue; + + /* See note in map.c:update_position about making this additive + * since light sources are never applied, need to put check here. + */ + if (tmp->glow_radius > glow_radius) + glow_radius = tmp->glow_radius; + + /* For some things, we don't care what is equipped */ + if (tmp->type == SKILL) + { + /* Want to take the highest skill here. */ + if (IS_MANA_SKILL (tmp->subtype)) + { + if (!mana_obj) + mana_obj = tmp; + else if (tmp->level > mana_obj->level) + mana_obj = tmp; + } + + if (IS_GRACE_SKILL (tmp->subtype)) + { + if (!grace_obj) + grace_obj = tmp; + else if (tmp->level > grace_obj->level) + grace_obj = tmp; } } - /* Container objects are not meant to adjust a players, but other applied - * objects need to make adjustments. - * This block should handle all player specific changes - * The check for Praying is a bit of a hack - god given bonuses are put - * in the praying skill, and the player should always get those. - * It also means we need to put in additional checks for applied below, - * because the skill shouldn't count against body positions being used - * up, etc. - */ - if ((QUERY_FLAG(tmp,FLAG_APPLIED) && tmp->type!=CONTAINER && tmp->type!=CLOSE_CON) || - (tmp->type == SKILL && tmp->subtype == SK_PRAYING)) { - if(op->type==PLAYER) { - if (tmp->type == BOW) - op->contr->ranges[range_bow] = tmp; - - if (tmp->type == WAND || tmp->type == ROD || tmp->type==HORN) - op->contr->ranges[range_misc] = tmp; - - for(i=0;istats),i,get_attr_value(&(tmp->stats),i)); - - /* these are the items that currently can change digestion, regeneration, - * spell point recovery and mana point recovery. Seems sort of an arbitary - * list, but other items store other info into stats array. - */ - if ((tmp->type == EXPERIENCE) || (tmp->type == WEAPON) || - (tmp->type == ARMOUR) || (tmp->type == HELMET) || - (tmp->type == SHIELD) || (tmp->type == RING) || - (tmp->type == BOOTS) || (tmp->type == GLOVES) || - (tmp->type == AMULET ) || (tmp->type == GIRDLE) || - (tmp->type == BRACERS ) || (tmp->type == CLOAK) || - (tmp->type == DISEASE) || (tmp->type == FORCE) || - (tmp->type == SKILL)) { - op->contr->digestion += tmp->stats.food; - op->contr->gen_hp += tmp->stats.hp; - op->contr->gen_sp += tmp->stats.sp; - op->contr->gen_grace += tmp->stats.grace; - op->contr->gen_sp_armour+= tmp->gen_sp_armour; - op->contr->item_power += tmp->item_power; - } - } /* if this is a player */ + /* Container objects are not meant to adjust a players, but other applied + * objects need to make adjustments. + * This block should handle all player specific changes + * The check for Praying is a bit of a hack - god given bonuses are put + * in the praying skill, and the player should always get those. + * It also means we need to put in additional checks for applied below, + * because the skill shouldn't count against body positions being used + * up, etc. + */ + if ((tmp->flag [FLAG_APPLIED] + && tmp->type != CONTAINER + && tmp->type != CLOSE_CON) + || (tmp->type == SKILL + && tmp->subtype == SK_PRAYING)) + { + if (type == PLAYER) + { + contr->item_power += tmp->item_power; - /* Update slots used for items */ - if (QUERY_FLAG(tmp,FLAG_APPLIED)) { - for (i=0; ibody_used[i] += tmp->body_info[i]; + if (tmp == contr->combat_ob || tmp == contr->ranged_ob) + if (tmp != current_weapon + && (tmp->type != SKILL || tmp->subtype != SK_PRAYING) + && !tmp->flag [FLAG_CURSED] + && !tmp->flag [FLAG_DAMNED]) + continue; + + for (i = 0; i < NUM_STATS; i++) + change_attr_value (&stats, i, tmp->stats.stat (i)); + + /* These are the items that currently can change digestion, regeneration, + * spell point recovery and mana point recovery. Seems sort of an arbitary + * list, but other items store other info into stats array. + */ + if (tmp->type == WEAPON || tmp->type == BOW || + tmp->type == ARMOUR || tmp->type == HELMET || + tmp->type == SHIELD || tmp->type == RING || + tmp->type == BOOTS || tmp->type == GLOVES || + tmp->type == AMULET || tmp->type == GIRDLE || + tmp->type == BRACERS || tmp->type == CLOAK || + tmp->type == DISEASE || tmp->type == FORCE || + tmp->type == SKILL) + { + contr->digestion += tmp->stats.food; + contr->gen_hp += tmp->stats.hp; + contr->gen_sp += tmp->stats.sp; + contr->gen_grace += tmp->stats.grace; + contr->gen_sp_armour += tmp->gen_sp_armour; + } + } /* if this is a player */ + else + { + if (tmp->type == WEAPON) + current_weapon = tmp; } - if(tmp->type==SYMPTOM) { - speed_reduce_from_disease = tmp->last_sp / 100.0; - if(speed_reduce_from_disease ==0) speed_reduce_from_disease = 1; + /* Update slots used for items */ + if (QUERY_FLAG (tmp, FLAG_APPLIED)) + for (i = 0; i < NUM_BODY_LOCATIONS; i++) + slot[i].used += tmp->slot[i].info; + + if (tmp->type == SYMPTOM) + { + speed_reduce_from_disease = tmp->last_sp / 100.f; + + if (speed_reduce_from_disease == 0) + speed_reduce_from_disease = 1; } - /* Pos. and neg. protections are counted seperate (-> pro/vuln). - * (Negative protections are calculated extactly like positive.) - * Resistance from potions are treated special as well. If there's - * more than one potion-effect, the bigger prot.-value is taken. - */ - if (tmp->type != POTION) { - for (i=0; itype==POTION_EFFECT) { - if (potion_resist[i]) - potion_resist[i] = MAX(potion_resist[i], tmp->resist[i]); - else - potion_resist[i] = tmp->resist[i]; + /* Pos. and neg. protections are counted seperate (-> pro/vuln). + * (Negative protections are calculated exactly like positive.) + * Resistance from potions are treated special as well. If there's + * more than one potion-effect, the bigger prot.-value is taken. + */ + if (tmp->type != POTION) + { + for (i = 0; i < NROFATTACKS; i++) + { + /* Potential for cursed potions, in which case we just can use + * a straight MAX, as potion_resist is initialised to zero. + */ + if (tmp->type == POTION_EFFECT) + { + if (potion_resist[i]) + potion_resist[i] = MAX (potion_resist[i], tmp->resist[i]); + else + potion_resist[i] = tmp->resist[i]; } - else if (tmp->resist[i] > 0) - prot[i] += ((100-prot[i])*tmp->resist[i])/100; - else if (tmp->resist[i] < 0) - vuln[i] += ((100-vuln[i])*(-tmp->resist[i]))/100; + else if (tmp->resist[i] > 0) + prot[i] += ((100 - prot[i]) * tmp->resist[i]) / 100; + else if (tmp->resist[i] < 0) + vuln[i] += ((100 - vuln[i]) * -tmp->resist[i]) / 100; } } - /* There may be other things that should not adjust the attacktype */ - if (tmp->type!=BOW && tmp->type != SYMPTOM) - op->attacktype|=tmp->attacktype; - - op->path_attuned|=tmp->path_attuned; - op->path_repelled|=tmp->path_repelled; - op->path_denied|=tmp->path_denied; - op->stats.luck+=tmp->stats.luck; - op->move_type |= tmp->move_type; - - if(QUERY_FLAG(tmp,FLAG_LIFESAVE)) SET_FLAG(op,FLAG_LIFESAVE); - if(QUERY_FLAG(tmp,FLAG_REFL_SPELL)) SET_FLAG(op,FLAG_REFL_SPELL); - if(QUERY_FLAG(tmp,FLAG_REFL_MISSILE)) SET_FLAG(op,FLAG_REFL_MISSILE); - if(QUERY_FLAG(tmp,FLAG_STEALTH)) SET_FLAG(op,FLAG_STEALTH); - if(QUERY_FLAG(tmp,FLAG_XRAYS)) SET_FLAG(op,FLAG_XRAYS); - if(QUERY_FLAG(tmp,FLAG_BLIND)) SET_FLAG(op,FLAG_BLIND); - if(QUERY_FLAG(tmp,FLAG_SEE_IN_DARK)) SET_FLAG(op,FLAG_SEE_IN_DARK); - - if(QUERY_FLAG(tmp,FLAG_UNDEAD)&&!QUERY_FLAG(&op->arch->clone,FLAG_UNDEAD)) - SET_FLAG(op,FLAG_UNDEAD); - - if(QUERY_FLAG(tmp,FLAG_MAKE_INVIS)) { - SET_FLAG(op,FLAG_MAKE_INVIS); - op->invisible=1; - } - - if(tmp->stats.exp && tmp->type!=SKILL) { - if(tmp->stats.exp > 0) { - added_speed+=(float)tmp->stats.exp/3.0; - bonus_speed+=1.0+(float)tmp->stats.exp/3.0; - } else - added_speed+=(float)tmp->stats.exp; - } - - switch(tmp->type) { - /* skills modifying the character -b.t. */ - /* for all skills and skill granting objects */ - case SKILL: - if (!QUERY_FLAG(tmp,FLAG_APPLIED)) break; + /* There may be other things that should not adjust the attacktype */ + if (tmp->type != SYMPTOM) + { + attacktype |= tmp->attacktype; + path_attuned |= tmp->path_attuned; + path_repelled |= tmp->path_repelled; + path_denied |= tmp->path_denied; + move_type |= tmp->move_type; + stats.luck += tmp->stats.luck; + } + + if (QUERY_FLAG (tmp, FLAG_LIFESAVE )) SET_FLAG (this, FLAG_LIFESAVE); + if (QUERY_FLAG (tmp, FLAG_REFL_SPELL )) SET_FLAG (this, FLAG_REFL_SPELL); + if (QUERY_FLAG (tmp, FLAG_REFL_MISSILE)) SET_FLAG (this, FLAG_REFL_MISSILE); + if (QUERY_FLAG (tmp, FLAG_STEALTH )) SET_FLAG (this, FLAG_STEALTH); + if (QUERY_FLAG (tmp, FLAG_XRAYS )) SET_FLAG (this, FLAG_XRAYS); + if (QUERY_FLAG (tmp, FLAG_BLIND )) SET_FLAG (this, FLAG_BLIND); + if (QUERY_FLAG (tmp, FLAG_SEE_IN_DARK )) SET_FLAG (this, FLAG_SEE_IN_DARK); - if (IS_COMBAT_SKILL(tmp->subtype)) wc_obj=tmp; + if (QUERY_FLAG (tmp, FLAG_UNDEAD) && !QUERY_FLAG (arch, FLAG_UNDEAD)) + SET_FLAG (this, FLAG_UNDEAD); - if (op->chosen_skill) { - LOG(llevDebug, "fix_player, op %s has multiple skills applied\n", &op->name); - } - op->chosen_skill = tmp; - if(tmp->stats.dam>0) { /* skill is a 'weapon' */ - if(!QUERY_FLAG(op,FLAG_READY_WEAPON)) - weapon_speed = (int) WEAPON_SPEED(tmp); - if(weapon_speed<0) weapon_speed = 0; - weapon_weight=tmp->weight; - op->stats.dam+=tmp->stats.dam*(1 + (op->chosen_skill->level/9)); - if(tmp->magic) op->stats.dam += tmp->magic; - } - if(tmp->stats.wc) - wc-=(tmp->stats.wc+tmp->magic); + if (QUERY_FLAG (tmp, FLAG_MAKE_INVIS)) + { + SET_FLAG (this, FLAG_MAKE_INVIS); + invisible = 1; + } + + if (tmp->stats.exp && tmp->type != SKILL) + { + if (tmp->stats.exp > 0) + { + added_speed += tmp->stats.exp / 3.f; + bonus_speed += tmp->stats.exp / 3.f + 1.f; + } + else + added_speed += tmp->stats.exp; + } - if (tmp->slaying!=NULL) - op->slaying = tmp->slaying; + switch (tmp->type) + { +#if 0 + case WAND: + case ROD: + case HORN: + if (type != PLAYER || current_weapon == tmp) + chosen_skill = tmp; + break; +#endif - if(tmp->stats.ac) - ac-=(tmp->stats.ac+tmp->magic); - if(settings.spell_encumbrance == TRUE && op->type==PLAYER) - op->contr->encumbrance+=(int)3*tmp->weight/1000; - if (op->type == PLAYER) - op->contr->ranges[range_skill] = op; + /* skills modifying the character -b.t. */ + /* for all skills and skill granting objects */ + case SKILL: + { + if (!QUERY_FLAG (tmp, FLAG_APPLIED) || skill_flags [tmp->subtype] & SF_APPLY) break; - case SKILL_TOOL: - if (op->chosen_skill) { - LOG(llevDebug, "fix_player, op %s has multiple skills applied\n", &op->name); + if (chosen_skill) + { + LOG (llevDebug, "fix_player, op %s has multiple skills applied (%s and %s)\n", + &name, &chosen_skill->name, &tmp->name); + + tmp->flag [FLAG_APPLIED] = false; + update_stats (); + return; } - op->chosen_skill = tmp; - if (op->type == PLAYER) - op->contr->ranges[range_skill] = op; - break; + else + chosen_skill = tmp; - case SHIELD: - if(settings.spell_encumbrance == TRUE && op->type==PLAYER) - op->contr->encumbrance+=(int)tmp->weight/2000; - case RING: - case AMULET: - case GIRDLE: - case HELMET: - case BOOTS: - case GLOVES: - case CLOAK: - if(tmp->stats.wc) - wc-=(tmp->stats.wc+tmp->magic); - if(tmp->stats.dam) - op->stats.dam+=(tmp->stats.dam+tmp->magic); - if(tmp->stats.ac) - ac-=(tmp->stats.ac+tmp->magic); - break; + if (tmp->stats.dam > 0) + { /* skill is a 'weapon' */ + if (!QUERY_FLAG (this, FLAG_READY_WEAPON)) + weapon_speed = WEAPON_SPEED (tmp); + + if (weapon_speed < 0) + weapon_speed = 0; + + weapon_weight = tmp->weight; + stats.dam += 1 + chosen_skill->level * tmp->stats.dam / 9; + + if (tmp->magic) + stats.dam += tmp->magic; + } + + if (tmp->stats.wc) + wc -= tmp->stats.wc + tmp->magic; + + if (tmp->slaying) + slaying = tmp->slaying; + + if (tmp->stats.ac) + ac -= tmp->stats.ac + tmp->magic; + + if (settings.spell_encumbrance == TRUE && type == PLAYER) + contr->encumbrance += (int) 3 *tmp->weight / 1000; + } + + break; + + case SHIELD: + if (settings.spell_encumbrance == TRUE && type == PLAYER) + contr->encumbrance += (int) tmp->weight / 2000; + case RING: + case AMULET: + case GIRDLE: + case HELMET: + case BOOTS: + case GLOVES: + case CLOAK: + if (tmp->stats.wc) + wc -= tmp->stats.wc + tmp->magic; + + if (tmp->stats.dam) + stats.dam += tmp->stats.dam + tmp->magic; + + if (tmp->stats.ac) + ac -= tmp->stats.ac + tmp->magic; + + break; + + case BOW: + case WEAPON: + if (type != PLAYER || current_weapon == tmp) + { + wc -= tmp->stats.wc + tmp->magic; + + if (tmp->stats.ac && tmp->stats.ac + tmp->magic > 0) + ac -= tmp->stats.ac + tmp->magic; + + stats.dam += tmp->stats.dam + tmp->magic; + weapon_weight = tmp->weight; + weapon_speed = (WEAPON_SPEED (tmp) * 2 - tmp->magic) / 2; + + if (weapon_speed < 0) + weapon_speed = 0; + + slaying = tmp->slaying; - case WEAPON: - wc-=(tmp->stats.wc+tmp->magic); - if(tmp->stats.ac&&tmp->stats.ac+tmp->magic>0) - ac-=tmp->stats.ac+tmp->magic; - op->stats.dam+=(tmp->stats.dam+tmp->magic); - weapon_weight=tmp->weight; - weapon_speed=((int)WEAPON_SPEED(tmp)*2-tmp->magic)/2; - if(weapon_speed<0) weapon_speed=0; - op->slaying = tmp->slaying; /* If there is desire that two handed weapons should do * extra strength damage, this is where the code should * go. */ - op->current_weapon = tmp; - if(settings.spell_encumbrance == TRUE && op->type==PLAYER) - op->contr->encumbrance+=(int)3*tmp->weight/1000; - break; - case ARMOUR: /* Only the best of these three are used: */ - if(settings.spell_encumbrance == TRUE && op->type==PLAYER) - op->contr->encumbrance+=(int)tmp->weight/1000; - - case BRACERS: - case FORCE: - if(tmp->stats.wc) { - if(best_wcstats.wc+tmp->magic) { - wc+=best_wc; - best_wc=tmp->stats.wc+tmp->magic; - } else - wc+=tmp->stats.wc+tmp->magic; - } - if(tmp->stats.ac) { - if(best_acstats.ac+tmp->magic) { - ac+=best_ac; /* Remove last bonus */ - best_ac=tmp->stats.ac+tmp->magic; - } - else /* To nullify the below effect */ - ac+=tmp->stats.ac+tmp->magic; - } - if(tmp->stats.wc) wc-=(tmp->stats.wc+tmp->magic); - if(tmp->stats.ac) ac-=(tmp->stats.ac+tmp->magic); - if(ARMOUR_SPEED(tmp)&&ARMOUR_SPEED(tmp)/10.0encumbrance += tmp->weight * 3 / 1000; + } + break; - } /* switch tmp->type */ - } /* item is equipped */ - } /* for loop of items */ - - /* We've gone through all the objects the player has equipped. For many things, we - * have generated intermediate values which we now need to assign. - */ - - /* 'total resistance = total protections - total vulnerabilities'. - * If there is an uncursed potion in effect, granting more protection - * than that, we take: 'total resistance = resistance from potion'. - * If there is a cursed (and no uncursed) potion in effect, we take - * 'total resistance = vulnerability from cursed potion'. - */ - for (i=0; iresist[i] = prot[i] - vuln[i]; - if (potion_resist[i] && ((potion_resist[i] > op->resist[i]) || - (potion_resist[i] < 0))) - op->resist[i] = potion_resist[i]; - } - - /* Figure out the players sp/mana/hp totals. */ - if(op->type==PLAYER) { - int pl_level; - check_stat_bounds(&(op->stats)); - pl_level=op->level; + case ARMOUR: /* Only the best of these three are used: */ + if (settings.spell_encumbrance == TRUE && type == PLAYER) + contr->encumbrance += tmp->weight / 1000; + + case BRACERS: + case FORCE: + if (tmp->stats.wc) + { + if (best_wc < tmp->stats.wc + tmp->magic) + { + wc += best_wc; + best_wc = tmp->stats.wc + tmp->magic; + } + else + wc += tmp->stats.wc + tmp->magic; + } + + if (tmp->stats.ac) + { + if (best_ac < tmp->stats.ac + tmp->magic) + { + ac += best_ac; /* Remove last bonus */ + best_ac = tmp->stats.ac + tmp->magic; + } + else /* To nullify the below effect */ + ac += tmp->stats.ac + tmp->magic; + } - if(pl_level<1) pl_level=1; /* safety, we should always get 1 levels worth of hp! */ + if (tmp->stats.wc) + wc -= (tmp->stats.wc + tmp->magic); - /* You basically get half a con bonus/level. But we do take into account rounding, - * so if your bonus is 7, you still get 7 worth of bonus every 2 levels. - */ - for(i=1,op->stats.maxhp=0;i<=pl_level&&i<=10;i++) { - j=op->contr->levhp[i]+con_bonus[op->stats.Con]/2; - if(i%2 && con_bonus[op->stats.Con]%2) { - if (con_bonus[op->stats.Con]>0) - j++; - else - j--; - } - op->stats.maxhp+=j>1?j:1; /* always get at least 1 hp/level */ - } - - for(i=11;i<=op->level;i++) - op->stats.maxhp+=2; - - if(op->stats.hp>op->stats.maxhp) - op->stats.hp=op->stats.maxhp; - - /* Sp gain is controlled by the level of the player's - * relevant experience object (mana_obj, see above) - */ - /* following happen when skills system is not used */ - if(!mana_obj) mana_obj = op; - if(!grace_obj) grace_obj = op; - /* set maxsp */ - if(!mana_obj || !mana_obj->level || op->type!=PLAYER) mana_obj = op; - - if (mana_obj == op && op->type == PLAYER) { - op->stats.maxsp = 1; - } else { - sp_tmp=0.0; - for(i=1;i<=mana_obj->level&&i<=10;i++) { - float stmp; - - /* Got some extra bonus at first level */ - if(i<2) { - stmp = op->contr->levsp[i] +((2.0 * (float)sp_bonus[op->stats.Pow] + - (float)sp_bonus[op->stats.Int])/6.0); - } else { - stmp=(float)op->contr->levsp[i] - +(2.0 * (float)sp_bonus[op->stats.Pow] + - (float)sp_bonus[op->stats.Int])/12.0; - } - if (stmp<1.0) stmp=1.0; - sp_tmp+=stmp; + if (tmp->stats.ac) + ac -= (tmp->stats.ac + tmp->magic); + + if (ARMOUR_SPEED (tmp) && ARMOUR_SPEED (tmp) / 10.f < max) + max = ARMOUR_SPEED (tmp) / 10.f; + + break; + } /* switch tmp->type */ + } /* item is equipped */ + } /* for loop of items */ + + /* We've gone through all the objects the player has equipped. For many things, we + * have generated intermediate values which we now need to assign. + */ + + /* 'total resistance = total protections - total vulnerabilities'. + * If there is an uncursed potion in effect, granting more protection + * than that, we take: 'total resistance = resistance from potion'. + * If there is a cursed (and no uncursed) potion in effect, we take + * 'total resistance = vulnerability from cursed potion'. + */ + for (i = 0; i < NROFATTACKS; i++) + { + resist[i] = prot[i] - vuln[i]; + + if (potion_resist[i] && ((potion_resist[i] > resist[i]) || (potion_resist[i] < 0))) + resist[i] = potion_resist[i]; + } + + /* Figure out the players sp/mana/hp totals. */ + if (type == PLAYER) + { + int pl_level; + + check_stat_bounds (&(stats)); + pl_level = level; + + if (pl_level < 1) + pl_level = 1; /* safety, we should always get 1 levels worth of hp! */ + + /* You basically get half a con bonus/level. But we do take into account rounding, + * so if your bonus is 7, you still get 7 worth of bonus every 2 levels. + */ + for (i = 1, stats.maxhp = 0; i <= pl_level && i <= 10; i++) + { + j = contr->levhp[i] + con_bonus[stats.Con] / 2; + + if (i % 2 && con_bonus[stats.Con] % 2) + { + if (con_bonus[stats.Con] > 0) + j++; + else + j--; } - op->stats.maxsp=(int)sp_tmp; - for(i=11;i<=mana_obj->level;i++) - op->stats.maxsp+=2; + stats.maxhp += j > 1 ? j : 1; /* always get at least 1 hp/level */ } - /* Characters can get their sp supercharged via rune of transferrance */ - if(op->stats.sp>op->stats.maxsp*2) - op->stats.sp=op->stats.maxsp*2; - - /* set maxgrace, notice 3-4 lines below it depends on both Wis and Pow */ - if(!grace_obj || !grace_obj->level || op->type!=PLAYER) grace_obj = op; - - if (grace_obj == op && op->type == PLAYER) { - op->stats.maxgrace = 1; - } else { - /* store grace in a float - this way, the divisions below don't create - * big jumps when you go from level to level - with int's, it then - * becomes big jumps when the sums of the bonuses jump to the next - * step of 8 - with floats, even fractional ones are useful. - */ - sp_tmp=0.0; - for(i=1,op->stats.maxgrace=0;i<=grace_obj->level&&i<=10;i++) { - float grace_tmp=0.0; - - /* Got some extra bonus at first level */ - if(i<2) { - grace_tmp = op->contr->levgrace[i]+(((float)grace_bonus[op->stats.Pow] + - 2.0 * (float)grace_bonus[op->stats.Wis])/6.0); - } else { - grace_tmp=(float)op->contr->levgrace[i] - +((float)grace_bonus[op->stats.Pow] + - 2.0 * (float)grace_bonus[op->stats.Wis])/12.0; - } - if (grace_tmp<1.0) grace_tmp=1.0; - sp_tmp+=grace_tmp; + + for (i = 11; i <= level; i++) + stats.maxhp += 2; + + if (stats.hp > stats.maxhp) + stats.hp = stats.maxhp; + + /* Sp gain is controlled by the level of the player's + * relevant experience object (mana_obj, see above) + */ + /* following happen when skills system is not used */ + if (!mana_obj) + mana_obj = this; + + if (!grace_obj) + grace_obj = this; + + /* set maxsp */ + if (!mana_obj || !mana_obj->level || type != PLAYER) + mana_obj = this; + + if (mana_obj == this && type == PLAYER) + stats.maxsp = 1; + else + { + sp_tmp = 0.f; + + for (i = 1; i <= mana_obj->level && i <= 10; i++) + { + float stmp; + + /* Got some extra bonus at first level */ + if (i < 2) + stmp = contr->levsp[i] + (2.f * sp_bonus[stats.Pow] + sp_bonus[stats.Int]) / 6.f; + else + stmp = contr->levsp[i] + (2.f * sp_bonus[stats.Pow] + sp_bonus[stats.Int]) / 12.f; + + if (stmp < 1.f) + stmp = 1.f; + + sp_tmp += stmp; } - op->stats.maxgrace=(int)sp_tmp; - /* two grace points per level after 11 */ - for(i=11;i<=grace_obj->level;i++) - op->stats.maxgrace+=2; + stats.maxsp = (sint16)sp_tmp; + + for (i = 11; i <= mana_obj->level; i++) + stats.maxsp += 2; } - /* No limit on grace vs maxgrace */ - if(op->contr->braced) { - ac+=2; - wc+=4; + /* Characters can get their sp supercharged via rune of transferrance */ + if (stats.sp > stats.maxsp * 2) + stats.sp = stats.maxsp * 2; + + /* set maxgrace, notice 3-4 lines below it depends on both Wis and Pow */ + if (!grace_obj || !grace_obj->level || type != PLAYER) + grace_obj = this; + + if (grace_obj == this && type == PLAYER) + stats.maxgrace = 1; + else + { + /* store grace in a float - this way, the divisions below don't create + * big jumps when you go from level to level - with int's, it then + * becomes big jumps when the sums of the bonuses jump to the next + * step of 8 - with floats, even fractional ones are useful. + */ + sp_tmp = 0.f; + for (i = 1, stats.maxgrace = 0; i <= grace_obj->level && i <= 10; i++) + { + float grace_tmp = 0.f; + + /* Got some extra bonus at first level */ + if (i < 2) + grace_tmp = contr->levgrace[i] + (grace_bonus[stats.Pow] + 2.f * grace_bonus[stats.Wis]) / 6.f; + else + grace_tmp = contr->levgrace[i] + (grace_bonus[stats.Pow] + 2.f * grace_bonus[stats.Wis]) / 12.f; + + if (grace_tmp < 1.f) + grace_tmp = 1.f; + + sp_tmp += grace_tmp; + } + + stats.maxgrace = (sint16)sp_tmp; + + /* two grace points per level after 11 */ + for (i = 11; i <= grace_obj->level; i++) + stats.maxgrace += 2; } - else - ac-=dex_bonus[op->stats.Dex]; - /* In new exp/skills system, wc bonuses are related to - * the players level in a relevant exp object (wc_obj) - * not the general player level -b.t. - * I changed this slightly so that wc bonuses are better - * than before. This is to balance out the fact that - * the player no longer gets a personal weapon w/ 1 - * improvement every level, now its fighterlevel/5. So - * we give the player a bonus here in wc and dam - * to make up for the change. Note that I left the - * monster bonus the same as before. -b.t. - */ + /* No limit on grace vs maxgrace */ - if(op->type==PLAYER && wc_obj && wc_obj->level>1) { - wc-=(wc_obj->level+thaco_bonus[op->stats.Str]); - for(i=1;ilevel;i++) { - /* addtional wc every 6 levels */ - if(!(i%6)) wc--; - /* addtional dam every 4 levels. */ - if(!(i%4) && (dam_bonus[op->stats.Str]>=0)) - op->stats.dam+=(1+(dam_bonus[op->stats.Str]/5)); - } - } else - wc-=(op->level+thaco_bonus[op->stats.Str]); - - op->stats.dam+=dam_bonus[op->stats.Str]; - - if(op->stats.dam<1) - op->stats.dam=1; - - op->speed=1.0+speed_bonus[op->stats.Dex]; - if (settings.search_items && op->contr->search_str[0]) - op->speed -= 1; - if (op->attacktype==0) - op->attacktype=op->arch->clone.attacktype; - - } /* End if player */ - - if(added_speed>=0) - op->speed+=added_speed/10.0; - else /* Something wrong here...: */ - op->speed /= (float)(1.0-added_speed); - - /* Max is determined by armour */ - if(op->speed>max) - op->speed=max; - - if(op->type == PLAYER) { - /* f is a number the represents the number of kg above (positive num) - * or below (negative number) that the player is carrying. If above - * weight limit, then player suffers a speed reduction based on how - * much above he is, and what is max carry is - */ - f=(op->carrying/1000)-max_carry[op->stats.Str]; - if(f>0) op->speed=op->speed/(1.0+f/max_carry[op->stats.Str]); + if (contr->braced) + { + ac += 2; + wc += 4; + } + else + ac -= dex_bonus[stats.Dex]; + + /* In new exp/skills system, wc bonuses are related to + * the players level in a relevant exp object (wc_obj) + * not the general player level -b.t. + * I changed this slightly so that wc bonuses are better + * than before. This is to balance out the fact that + * the player no longer gets a personal weapon w/ 1 + * improvement every level, now its fighterlevel/5. So + * we give the player a bonus here in wc and dam + * to make up for the change. Note that I left the + * monster bonus the same as before. -b.t. + */ + object *wc_obj = chosen_skill; + + if (contr && wc_obj && wc_obj->level > 1) + { + wc -= wc_obj->level + thaco_bonus[stats.Str]; + + for (i = 1; i < wc_obj->level; i++) + { + /* additional wc every 6 levels */ + if (!(i % 6)) + wc--; + + /* additional dam every 4 levels. */ + if (!(i % 4) && dam_bonus[stats.Str] >= 0) + stats.dam += 1 + dam_bonus[stats.Str] / 5; + } + } + else + wc -= level + thaco_bonus[stats.Str]; + + stats.dam += dam_bonus[stats.Str]; + + if (stats.dam < 1) + stats.dam = 1; + + speed = 1.f + speed_bonus[stats.Dex]; + + if (settings.search_items && contr->search_str[0]) + speed -= 1; + + if (attacktype == 0) + attacktype = arch->attacktype; + } /* End if player */ + + if (added_speed >= 0) + speed += added_speed / 10.f; + else /* Something wrong here...: */ + speed /= 1.f - added_speed; + + /* Max is determined by armour */ + if (speed > max) + speed = max; + + if (type == PLAYER) + { + /* f is a number the represents the number of kg above (positive num) + * or below (negative number) that the player is carrying. If above + * weight limit, then player suffers a speed reduction based on how + * much above he is, and what is max carry is + */ + f = (carrying / 1000) - max_carry[stats.Str]; + if (f > 0) + speed = speed / (1.f + f / max_carry[stats.Str]); } - op->speed+=bonus_speed/10.0; /* Not affected by limits */ + speed += bonus_speed / 10.f; /* Not affected by limits */ - /* Put a lower limit on speed. Note with this speed, you move once every - * 100 ticks or so. This amounts to once every 12 seconds of realtime. - */ - op->speed = op->speed * speed_reduce_from_disease; + /* Put a lower limit on speed. Note with this speed, you move once every + * 100 ticks or so. This amounts to once every 12 seconds of realtime. + */ + speed = speed * speed_reduce_from_disease; - if (op->speed<0.01 && op->type==PLAYER) op->speed=0.01; + if (speed < 0.01f && type == PLAYER) + speed = 0.01f; - if(op->type == PLAYER) { - float M,W,s,D,K,S,M2; + if (type == PLAYER) + { + /* (This formula was made by vidarl@ifi.uio.no) + * Note that we never used these values again - basically + * all of these could be subbed into one big equation, but + * that would just be a real pain to read. + */ + float M = (max_carry[stats.Str] - 121) / 121.f; + float M2 = max_carry[stats.Str] / 100.f; + float W = weapon_weight / 20000.f; + float s = (20 - weapon_speed) / 10.f; + float D = (stats.Dex - 14) / 14.f; + float K = 1 + M / 3.f - W / (3 * M2) + speed / 5.f + D / 2.f; - /* (This formula was made by vidarl@ifi.uio.no) - * Note that we never used these values again - basically - * all of these could be subbed into one big equation, but - * that would just be a real pain to read. - */ - M=(max_carry[op->stats.Str]-121)/121.0; - M2=max_carry[op->stats.Str]/100.0; - W=weapon_weight/20000.0; - s=2-weapon_speed/10.0; - D=(op->stats.Dex-14)/14.0; - K=1 + M/3.0 - W/(3*M2) + op->speed/5.0 + D/2.0; - K*=(4+op->level)/(float)(6+op->level)*1.2; - if(K<=0) K=0.01; - S=op->speed/(K*s); - op->contr->weapon_sp=S; - } - /* I want to limit the power of small monsters with big weapons: */ - if(op->type!=PLAYER&&op->arch!=NULL&& - op->stats.dam>op->arch->clone.stats.dam*3) - op->stats.dam=op->arch->clone.stats.dam*3; - - /* Prevent overflows of wc - best you can get is ABS(120) - this - * should be more than enough - remember, AC is also in 8 bits, - * so its value is the same. - */ - if (wc>120) wc=120; - else if (wc<-120) wc=-120; - op->stats.wc=wc; - - if (ac>120) ac=120; - else if (ac<-120) ac=-120; - op->stats.ac=ac; - - /* if for some reason the creature doesn't have any move type, - * give them walking as a default. - * The second case is a special case - to more closely mimic the - * old behaviour - if your flying, your not walking - just - * one or the other. - */ - if (op->move_type == 0) op->move_type = MOVE_WALK; - else if (op->move_type & (MOVE_FLY_LOW | MOVE_FLY_HIGH)) op->move_type &= ~MOVE_WALK; - - update_ob_speed(op); - - /* It is quite possible that a player's spell costing might have changed, - * so we will check that now. - */ - if (op->type == PLAYER) esrv_update_spells(op->contr); + K *= (4 + level) * 1.2f / (6 + level); + + if (K <= 0.01f) + K = 0.01f; + + contr->weapon_sp = K * s * .5f; //TODO: balance the .5 + } + + /* I want to limit the power of small monsters with big weapons: */ + if (type != PLAYER && arch && stats.dam > arch->stats.dam * 3) + stats.dam = arch->stats.dam * 3; + + stats.wc = clamp (wc, MIN_WC, MAX_WC); + stats.ac = clamp (ac, MIN_AC, MAX_AC); + + /* if for some reason the creature doesn't have any move type, + * give them walking as a default. + * The second case is a special case - to more closely mimic the + * old behaviour - if your flying, your not walking - just + * one or the other. + */ + if (move_type == 0) + move_type = MOVE_WALK; + else if (move_type & (MOVE_FLY_LOW | MOVE_FLY_HIGH)) + move_type &= ~MOVE_WALK; + + if (speed != old_speed) + set_speed (speed); + + /* It is quite possible that a player's spell costing might have changed, + * so we will check that now. + */ + if (type == PLAYER) + { + esrv_update_stats (contr); + esrv_update_spells (contr); + } + + // update the mapspace, if we are on a map + if (!flag [FLAG_REMOVED] && map) + map->at (x, y).flags_ = 0; } /* @@ -1355,11 +1421,16 @@ * merely checks that all stats are 1 or more, and returns * false otherwise. */ - -int allowed_class(const object *op) { - return op->stats.Dex>0&&op->stats.Str>0&&op->stats.Con>0&& - op->stats.Int>0&&op->stats.Wis>0&&op->stats.Pow>0&& - op->stats.Cha>0; +int +allowed_class (const object *op) +{ + return op->stats.Dex > 0 + && op->stats.Str > 0 + && op->stats.Con > 0 + && op->stats.Int > 0 + && op->stats.Wis > 0 + && op->stats.Pow > 0 + && op->stats.Cha > 0; } /* @@ -1372,53 +1443,58 @@ * as soon as clients support this! * Please, anyone, write support for 'ext_title'. */ -void set_dragon_name(object *pl, const object *abil, const object *skin) { - int atnr=-1; /* attacknumber of highest level */ - int level=0; /* highest level */ +void +set_dragon_name (object *pl, const object *abil, const object *skin) +{ + int atnr = -1; /* attacknumber of highest level */ + int level = 0; /* highest level */ int i; /* Perhaps do something more clever? */ - if (!abil || !skin) return; - + if (!abil || !skin) + return; + /* first, look for the highest level */ - for(i=0; iresist[i] > abil->resist[atnr])) { - level = abil->resist[i]; - atnr = i; + for (i = 0; i < NROFATTACKS; i++) + { + if (atnr_is_dragon_enabled (i) && (atnr == -1 || abil->resist[i] > abil->resist[atnr])) + { + level = abil->resist[i]; + atnr = i; + } } - } - + /* now if there are equals at highest level, pick the one with focus, or else at random */ - if (atnr_is_dragon_enabled(abil->stats.exp) && - abil->resist[abil->stats.exp] >= level) + if (atnr_is_dragon_enabled (abil->stats.exp) && abil->resist[abil->stats.exp] >= level) atnr = abil->stats.exp; - - level = (int)(level/5.); - + + level = (int) (level / 5.); + /* now set the new title */ - if (pl->contr != NULL) { - if(level == 0) - sprintf(pl->contr->title, "%s hatchling", attacks[atnr]); - else if (level == 1) - sprintf(pl->contr->title, "%s wyrm", attacks[atnr]); - else if (level == 2) - sprintf(pl->contr->title, "%s wyvern", attacks[atnr]); - else if (level == 3) - sprintf(pl->contr->title, "%s dragon", attacks[atnr]); - else { - /* special titles for extra high resistance! */ - if (skin->resist[atnr] > 80) - sprintf(pl->contr->title, "legendary %s dragon", attacks[atnr]); - else if (skin->resist[atnr] > 50) - sprintf(pl->contr->title, "ancient %s dragon", attacks[atnr]); + if (pl->contr != NULL) + { + if (level == 0) + sprintf (pl->contr->title, "%s hatchling", attacks[atnr]); + else if (level == 1) + sprintf (pl->contr->title, "%s wyrm", attacks[atnr]); + else if (level == 2) + sprintf (pl->contr->title, "%s wyvern", attacks[atnr]); + else if (level == 3) + sprintf (pl->contr->title, "%s dragon", attacks[atnr]); else - sprintf(pl->contr->title, "big %s dragon", attacks[atnr]); + { + /* special titles for extra high resistance! */ + if (skin->resist[atnr] > 80) + sprintf (pl->contr->title, "legendary %s dragon", attacks[atnr]); + else if (skin->resist[atnr] > 50) + sprintf (pl->contr->title, "ancient %s dragon", attacks[atnr]); + else + sprintf (pl->contr->title, "big %s dragon", attacks[atnr]); + } } - } - - strcpy(pl->contr->own_title, ""); + + strcpy (pl->contr->own_title, ""); } /* @@ -1426,53 +1502,56 @@ * an overall level. Here, the dragon might gain new abilities * or change the ability-focus. */ -void dragon_level_gain(object *who) { - object *abil = NULL; /* pointer to dragon ability force*/ - object *skin = NULL; /* pointer to dragon skin force*/ - object *tmp = NULL; /* tmp. object */ - char buf[MAX_BUF]; /* tmp. string buffer */ - - /* now grab the 'dragon_ability'-forces from the player's inventory */ - for (tmp=who->inv; tmp!=NULL; tmp=tmp->below) { - if (tmp->type == FORCE) { - if (strcmp(tmp->arch->name, "dragon_ability_force")==0) - abil = tmp; - if (strcmp(tmp->arch->name, "dragon_skin_force")==0) - skin = tmp; - } - } - /* if the force is missing -> bail out */ - if (abil == NULL) return; - - /* The ability_force keeps track of maximum level ever achieved. - * New abilties can only be gained by surpassing this max level - */ - if (who->level > abil->level) { - /* increase our focused ability */ - abil->resist[abil->stats.exp]++; - - - if (abil->resist[abil->stats.exp]>0 && abil->resist[abil->stats.exp]%5 == 0) { - /* time to hand out a new ability-gift */ - dragon_ability_gain(who, (int)abil->stats.exp, - (int)((1+abil->resist[abil->stats.exp])/5.)); - } - - if (abil->last_eat > 0 && atnr_is_dragon_enabled(abil->last_eat)) { - /* apply new ability focus */ - sprintf(buf, "Your metabolism now focuses on %s!", - change_resist_msg[abil->last_eat]); - new_draw_info(NDI_UNIQUE|NDI_BLUE, 0, who, buf); - - abil->stats.exp = abil->last_eat; - abil->last_eat = 0; - } - - abil->level = who->level; - } - - /* last but not least, set the new title for the dragon */ - set_dragon_name(who, abil, skin); +void +dragon_level_gain (object *who) +{ + object *abil = NULL; /* pointer to dragon ability force */ + object *skin = NULL; /* pointer to dragon skin force */ + object *tmp = NULL; /* tmp. object */ + char buf[MAX_BUF]; /* tmp. string buffer */ + + /* now grab the 'dragon_ability'-forces from the player's inventory */ + for (tmp = who->inv; tmp; tmp = tmp->below) + if (tmp->type == FORCE) + if (tmp->arch->archname == shstr_dragon_ability_force) + abil = tmp; + else if (tmp->arch->archname == shstr_dragon_skin_force) + skin = tmp; + + /* if the force is missing -> bail out */ + if (abil == NULL) + return; + + /* The ability_force keeps track of maximum level ever achieved. + * New abilties can only be gained by surpassing this max level + */ + if (who->level > abil->level) + { + /* increase our focused ability */ + abil->resist[abil->stats.exp]++; + + + if (abil->resist[abil->stats.exp] > 0 && abil->resist[abil->stats.exp] % 5 == 0) + { + /* time to hand out a new ability-gift */ + dragon_ability_gain (who, (int) abil->stats.exp, (int) ((1 + abil->resist[abil->stats.exp]) / 5.)); + } + + if (abil->last_eat > 0 && atnr_is_dragon_enabled (abil->last_eat)) + { + /* apply new ability focus */ + sprintf (buf, "Your metabolism now focuses on %s!", change_resist_msg[abil->last_eat]); + new_draw_info (NDI_UNIQUE | NDI_BLUE, 0, who, buf); + + abil->stats.exp = abil->last_eat; + abil->last_eat = 0; + } + + abil->level = who->level; + } + + /* last but not least, set the new title for the dragon */ + set_dragon_name (who, abil, skin); } /* Handy function - given the skill name skill_name, we find the skill @@ -1481,29 +1560,35 @@ * We return the skill - this makes it easier for calling functions that * want to do something with it immediately. */ -object *give_skill_by_name(object *op, const char *skill_name) +object * +give_skill_by_name (object *op, const char *skill_name) { - object *skill_obj; + object *skill_obj; - skill_obj = get_archetype_by_skill_name(skill_name, SKILL); - if (!skill_obj) { - LOG(llevError, "add_player_exp: couldn't find skill %s\n", skill_name); - return NULL; - } - /* clear the flag - exp goes into this bucket, but player - * still doesn't know it. - */ - CLEAR_FLAG(skill_obj, FLAG_CAN_USE_SKILL); - skill_obj->stats.exp = 0; - skill_obj->level = 1; - insert_ob_in_ob(skill_obj, op); - if (op->contr) { - op->contr->last_skill_ob[skill_obj->subtype] = skill_obj; - op->contr->last_skill_exp[skill_obj->subtype] = -1; + skill_obj = get_archetype_by_skill_name (skill_name, SKILL); + if (!skill_obj) + { + LOG (llevError, "add_player_exp: couldn't find skill %s\n", skill_name); + return NULL; } - return skill_obj; -} + /* clear the flag - exp goes into this bucket, but player + * still doesn't know it. + */ + CLEAR_FLAG (skill_obj, FLAG_CAN_USE_SKILL); + skill_obj->stats.exp = 0; + skill_obj->level = 1; + insert_ob_in_ob (skill_obj, op); + + if (player *pl = op->contr) + { + pl->last_skill_ob [skill_obj->subtype] = skill_obj; + if (pl->ns) + pl->ns->last_skill_exp[skill_obj->subtype] = -1;//TODO: should be made superfluous + } + + return skill_obj; +} /* player_lvl_adj() - for the new exp system. we are concerned with * whether the player gets more hp, sp and new levels. @@ -1512,45 +1597,60 @@ * who is the player, op is what we are checking to gain the level * (eg, skill) */ -void player_lvl_adj(object *who, object *op) { - char buf[MAX_BUF]; - - if(!op) /* when rolling stats */ - op = who; - - if(op->level < settings.max_level && op->stats.exp >= level_exp(op->level+1,who->expmul)) { - op->level++; - - if (op != NULL && op == who && op->stats.exp > 1 && is_dragon_pl(who)) - dragon_level_gain(who); - - /* Only roll these if it is the player (who) that gained the level */ - if(op==who && (who->level < 11) && who->type==PLAYER) { - who->contr->levhp[who->level] = die_roll(2, 4, who, PREFER_HIGH)+1; - who->contr->levsp[who->level] = die_roll(2, 3, who, PREFER_HIGH); - who->contr->levgrace[who->level]=die_roll(2, 2, who, PREFER_HIGH)-1; - } - - fix_player(who); - if(op->level>1) { - if (op->type!=PLAYER) - sprintf(buf,"You are now level %d in the %s skill.",op->level,&op->name); - else - sprintf(buf,"You are now level %d.",op->level); - if(who) new_draw_info(NDI_UNIQUE|NDI_RED, 0, who,buf); - } - player_lvl_adj(who,op); /* To increase more levels */ - } else if (op->level>1 && op->stats.explevel,who->expmul)) { - op->level--; - fix_player(who); - if(op->type!=PLAYER) { - sprintf(buf,"You are now level %d in the %s skill.",op->level,&op->name); - new_draw_info(NDI_UNIQUE|NDI_RED, 0, who,buf); +void +player_lvl_adj (object *who, object *op) +{ + char buf[MAX_BUF]; + + if (!op) /* when rolling stats */ + op = who; + + if (op->level < settings.max_level && op->stats.exp >= level_exp (op->level + 1, who->expmul)) + { + op->level++; + + if (op && op == who && op->stats.exp > 1 && is_dragon_pl (who)) + dragon_level_gain (who); + + /* Only roll these if it is the player (who) that gained the level */ + if (op == who && (who->level < 11) && who->type == PLAYER) + { + who->contr->levhp[who->level] = die_roll (2, 4, who, PREFER_HIGH) + 1; + who->contr->levsp[who->level] = die_roll (2, 3, who, PREFER_HIGH); + who->contr->levgrace[who->level] = die_roll (2, 2, who, PREFER_HIGH) - 1; } - player_lvl_adj(who,op); /* To decrease more levels */ + + who->update_stats (); + if (op->level > 1) + { + if (op->type != PLAYER) + sprintf (buf, "You are now level %d in the %s skill.", op->level, &op->name); + else + sprintf (buf, "You are now level %d.", op->level); + + if (who) + new_draw_info (NDI_UNIQUE | NDI_RED, 0, who, buf); + } + + player_lvl_adj (who, op); /* To increase more levels */ } - /* check if the spell data has changed */ - esrv_update_spells(who->contr); + else if (op->level > 1 && op->stats.exp < level_exp (op->level, who->expmul)) + { + op->level--; + who->update_stats (); + + if (op->type != PLAYER) + { + sprintf (buf, "You are now level %d in the %s skill.", op->level, &op->name); + new_draw_info (NDI_UNIQUE | NDI_RED, 0, who, buf); + } + + player_lvl_adj (who, op); /* To decrease more levels */ + } + + /* check if the spell data has changed */ + esrv_update_stats (who->contr); + esrv_update_spells (who->contr); } /* @@ -1558,10 +1658,13 @@ * the given level. level should really never exceed max_level */ -sint64 level_exp(int level,double expmul) { - if (level > settings.max_level) - return (sint64) (expmul * levels[settings.max_level]); - return (sint64) (expmul * levels[level]); +sint64 +level_exp (int level, double expmul) +{ + if (level > settings.max_level) + return (sint64) (expmul * levels[settings.max_level]); + + return (sint64) (expmul * levels[level]); } /* @@ -1571,25 +1674,25 @@ * has been reduced. Since there is now a proper field for perm_exp, * this can now work on a much larger set of objects. */ -void calc_perm_exp(object *op) +void +calc_perm_exp (object *op) { - int p_exp_min; - - /* Ensure that our permanent experience minimum is met. - * permenent_exp_ratio is an integer percentage, we divide by 100 - * to get the fraction */ - p_exp_min = (int)(settings.permanent_exp_ratio * (float)(op->stats.exp)/100); - - if (op->perm_exp < p_exp_min) - op->perm_exp = p_exp_min; - - /* Cap permanent experience. */ - if (op->perm_exp < 0) - op->perm_exp = 0; - else if (op->perm_exp > (sint64) MAX_EXPERIENCE) - op->perm_exp = MAX_EXPERIENCE; -} + int p_exp_min; + /* Ensure that our permanent experience minimum is met. + * permenent_exp_ratio is an integer percentage, we divide by 100 + * to get the fraction */ + p_exp_min = (int) (settings.permanent_exp_ratio * (float) (op->stats.exp) / 100); + + if (op->perm_exp < p_exp_min) + op->perm_exp = p_exp_min; + + /* Cap permanent experience. */ + if (op->perm_exp < 0) + op->perm_exp = 0; + else if (op->perm_exp > (sint64) MAX_EXPERIENCE) + op->perm_exp = MAX_EXPERIENCE; +} /* Add experience to a player - exp should only be positive. * Updates permanent exp for the skill we are adding to. @@ -1598,8 +1701,8 @@ * total, but not any particular skill. * flag is what to do if the player doesn't have the skill: */ - -static void add_player_exp (object * op, sint64 exp, const char *skill_name, int flag) +static void +add_player_exp (object *op, sint64 exp, const char *skill_name, int flag) { object *skill_obj = NULL; sint64 limit, exp_to_add; @@ -1607,23 +1710,21 @@ /* prevents some forms of abuse. */ if (op->contr->braced) - exp = exp / 5; + exp /= 5; /* Try to find the matching skill. * We do a shortcut/time saving mechanism first - see if it matches - * chosen_skill. This means we don't need to search through + * chosen_skill. This means we don't need to search through * the players inventory. */ if (skill_name) { - if (op->chosen_skill && op->chosen_skill->type == SKILL && - !strcmp (skill_name, op->chosen_skill->skill)) + if (op->chosen_skill && op->chosen_skill->type == SKILL && !strcmp (skill_name, op->chosen_skill->skill)) skill_obj = op->chosen_skill; else { for (i = 0; i < NUM_SKILLS; i++) - if (op->contr->last_skill_ob[i] && - !strcmp (op->contr->last_skill_ob[i]->skill, skill_name)) + if (op->contr->last_skill_ob[i] && !strcmp (op->contr->last_skill_ob[i]->skill, skill_name)) { skill_obj = op->contr->last_skill_ob[i]; break; @@ -1652,14 +1753,10 @@ if (exp_to_add > limit) exp_to_add = limit; - ADD_EXP (op->stats.exp, - (sint64) ((float) exp_to_add * - (skill_obj ? skill_obj->expmul : 1))); + ADD_EXP (op->stats.exp, (sint64) ((float) exp_to_add * (skill_obj ? skill_obj->expmul : 1))); if (settings.permanent_exp_ratio) { - ADD_EXP (op->perm_exp, - (sint64) ((float) exp_to_add * PERM_EXP_GAIN_RATIO * - (skill_obj ? skill_obj->expmul : 1))); + ADD_EXP (op->perm_exp, (sint64) ((float) exp_to_add * PERM_EXP_GAIN_RATIO * (skill_obj ? skill_obj->expmul : 1))); calc_perm_exp (op); } @@ -1676,8 +1773,7 @@ ADD_EXP (skill_obj->stats.exp, exp_to_add); if (settings.permanent_exp_ratio) { - skill_obj->perm_exp += - (sint64) ((float) exp_to_add * PERM_EXP_GAIN_RATIO); + skill_obj->perm_exp += (sint64) ((float) exp_to_add * PERM_EXP_GAIN_RATIO); calc_perm_exp (skill_obj); } @@ -1693,23 +1789,31 @@ * the 'exp' value passed should be positive - this is the * amount that should get subtract from the player. */ -sint64 check_exp_loss(const object *op, sint64 exp) +sint64 +check_exp_loss (const object *op, sint64 exp) { - sint64 del_exp; + sint64 del_exp; - if (exp > op->stats.exp) exp = op->stats.exp; - if (settings.permanent_exp_ratio) { - del_exp = (sint64) ((op->stats.exp - op->perm_exp) * PERM_EXP_MAX_LOSS_RATIO); - if (del_exp < 0) del_exp = 0; - if (exp > del_exp) exp=del_exp; + if (exp > op->stats.exp) + exp = op->stats.exp; + if (settings.permanent_exp_ratio) + { + del_exp = (sint64) ((op->stats.exp - op->perm_exp) * PERM_EXP_MAX_LOSS_RATIO); + if (del_exp < 0) + del_exp = 0; + if (exp > del_exp) + exp = del_exp; } - return exp; + return exp; } -sint64 check_exp_adjust(const object *op, sint64 exp) +sint64 +check_exp_adjust (const object *op, sint64 exp) { - if (exp<0) return check_exp_loss(op, exp); - else return MIN(exp, (sint64) MAX_EXPERIENCE - op->stats.exp); + if (exp < 0) + return check_exp_loss (op, exp); + else + return MIN (exp, (sint64) MAX_EXPERIENCE - op->stats.exp); } @@ -1725,35 +1829,40 @@ * exp is the amount of exp to subtract - thus, it should be * a postive number. */ -static void subtract_player_exp(object *op, sint64 exp, const char *skill, int flag) +static void +subtract_player_exp (object *op, sint64 exp, const char *skill, int flag) { - float fraction = (float) exp/(float) op->stats.exp; - object *tmp; - sint64 del_exp; - - for(tmp=op->inv;tmp;tmp=tmp->below) - if(tmp->type==SKILL && tmp->stats.exp) { - if (flag == SK_SUBTRACT_SKILL_EXP && skill && !strcmp(tmp->skill, skill)) { - del_exp = check_exp_loss(tmp, exp); - tmp->stats.exp -= del_exp; - player_lvl_adj(op, tmp); - } else if (flag != SK_SUBTRACT_SKILL_EXP) { - /* only want to process other skills if we are not trying - * to match a specific skill. - */ - del_exp = check_exp_loss(tmp, (sint64) (tmp->stats.exp * fraction)); - tmp->stats.exp -= del_exp; - player_lvl_adj(op, tmp); - } - } - if (flag != SK_SUBTRACT_SKILL_EXP) { - del_exp = check_exp_loss(op, exp); - op->stats.exp -= del_exp; - player_lvl_adj(op,NULL); - } -} + float fraction = (float) exp / (float) op->stats.exp; + object *tmp; + sint64 del_exp; + for (tmp = op->inv; tmp; tmp = tmp->below) + if (tmp->type == SKILL && tmp->stats.exp) + { + if (flag == SK_SUBTRACT_SKILL_EXP && skill && !strcmp (tmp->skill, skill)) + { + del_exp = check_exp_loss (tmp, exp); + tmp->stats.exp -= del_exp; + player_lvl_adj (op, tmp); + } + else if (flag != SK_SUBTRACT_SKILL_EXP) + { + /* only want to process other skills if we are not trying + * to match a specific skill. + */ + del_exp = check_exp_loss (tmp, (sint64) (tmp->stats.exp * fraction)); + tmp->stats.exp -= del_exp; + player_lvl_adj (op, tmp); + } + } + if (flag != SK_SUBTRACT_SKILL_EXP) + { + del_exp = check_exp_loss (op, exp); + op->stats.exp -= del_exp; + player_lvl_adj (op, NULL); + } +} /* change_exp() - changes experience to a player/monster. This * does bounds checking to make sure we don't overflow the max exp. @@ -1764,58 +1873,60 @@ * flag is what to do if player doesn't have the skill. * these last two values are only used for players. */ - -void change_exp(object *op, sint64 exp, const char *skill_name, int flag) { - +void +change_exp (object *op, sint64 exp, const char *skill_name, int flag) +{ #ifdef EXP_DEBUG -#ifndef WIN32 - LOG(llevDebug,"change_exp() called for %s, exp = %lld\n",query_name(op),exp); -#else - LOG(llevDebug,"change_exp() called for %s, exp = %I64d\n",query_name(op),exp); -#endif + LOG (llevDebug, "change_exp() called for %s, exp = %" PRId64 "\n", query_name (op), exp); #endif - /* safety */ - if(!op) { - LOG(llevError,"change_exp() called for null object!\n"); - return; - } - - /* if no change in exp, just return - most of the below code - * won't do anything if the value is 0 anyways. - */ - if (exp == 0) return; - - /* Monsters are easy - we just adjust their exp - we - * don't adjust level, since in most cases it is unrelated to - * the exp they have - the monsters exp represents what its - * worth. - */ - if(op->type != PLAYER) { - /* Sanity check */ - if (!QUERY_FLAG(op, FLAG_ALIVE)) return; - - /* reset exp to max allowed value. We subtract from - * MAX_EXPERIENCE to prevent overflows. If the player somehow has - * more than max exp, just return. - */ - if (exp > 0 && ( op->stats.exp > (sint64) (MAX_EXPERIENCE - exp))) { - exp = MAX_EXPERIENCE - op->stats.exp; - if (exp < 0) return; + /* safety */ + if (!op) + { + LOG (llevError, "change_exp() called for null object!\n"); + return; + } + + /* if no change in exp, just return - most of the below code + * won't do anything if the value is 0 anyways. + */ + if (exp == 0) + return; + + /* Monsters are easy - we just adjust their exp - we + * don't adjust level, since in most cases it is unrelated to + * the exp they have - the monsters exp represents what its + * worth. + */ + if (op->type != PLAYER) + { + /* Sanity check */ + if (!QUERY_FLAG (op, FLAG_ALIVE)) + return; + + /* reset exp to max allowed value. We subtract from + * MAX_EXPERIENCE to prevent overflows. If the player somehow has + * more than max exp, just return. + */ + if (exp > 0 && (op->stats.exp > (sint64) (MAX_EXPERIENCE - exp))) + { + exp = MAX_EXPERIENCE - op->stats.exp; + if (exp < 0) + return; } - op->stats.exp += exp; + op->stats.exp += exp; } - else { /* Players only */ - if(exp>0) - add_player_exp(op, exp, skill_name, flag); - else - /* note that when you lose exp, it doesn't go against - * a particular skill, so we don't need to pass that - * along. - */ - subtract_player_exp(op, FABS(exp), skill_name, flag); - + else + { /* Players only */ + if (exp > 0) + add_player_exp (op, exp, skill_name, flag); + else + /* note that when you lose exp, it doesn't go against + * a particular skill, so we don't need to pass that + * along. + */ + subtract_player_exp (op, abs (exp), skill_name, flag); } } @@ -1823,39 +1934,44 @@ * settings death_penalty_percentage and death_penalty_levels, and by the * amount of permenent experience, whichever gives the lowest loss. */ +void +apply_death_exp_penalty (object *op) +{ + object *tmp; + sint64 loss; + sint64 percentage_loss; /* defined by the setting 'death_penalty_percent' */ + sint64 level_loss; /* defined by the setting 'death_penalty_levels */ + + for (tmp = op->inv; tmp; tmp = tmp->below) + if (tmp->type == SKILL && tmp->stats.exp) + { + + percentage_loss = tmp->stats.exp * settings.death_penalty_ratio / 100; + level_loss = tmp->stats.exp - levels[MAX (0, tmp->level - settings.death_penalty_level)]; + + /* With the revised exp system, you can get cases where + * losing several levels would still require that you have more + * exp than you currently have - this is true if the levels + * tables is a lot harder. + */ + if (level_loss < 0) + level_loss = 0; -void apply_death_exp_penalty(object *op) { - object *tmp; - sint64 loss; - sint64 percentage_loss; /* defined by the setting 'death_penalty_percent' */ - sint64 level_loss; /* defined by the setting 'death_penalty_levels */ - - for(tmp=op->inv;tmp;tmp=tmp->below) - if(tmp->type==SKILL && tmp->stats.exp) { - - percentage_loss = tmp->stats.exp * settings.death_penalty_ratio/100; - level_loss = tmp->stats.exp - levels[MAX(0,tmp->level - settings.death_penalty_level)]; - - /* With the revised exp system, you can get cases where - * losing several levels would still require that you have more - * exp than you currently have - this is true if the levels - * tables is a lot harder. - */ - if (level_loss < 0) level_loss = 0; + loss = check_exp_loss (tmp, MIN (level_loss, percentage_loss)); - loss = check_exp_loss(tmp, MIN(level_loss, percentage_loss)); + tmp->stats.exp -= loss; + player_lvl_adj (op, tmp); + } - tmp->stats.exp -= loss; - player_lvl_adj(op,tmp); - } + percentage_loss = op->stats.exp * settings.death_penalty_ratio / 100; + level_loss = op->stats.exp - levels[MAX (0, op->level - settings.death_penalty_level)]; - percentage_loss = op->stats.exp * settings.death_penalty_ratio/100; - level_loss = op->stats.exp - levels[MAX(0,op->level - settings.death_penalty_level)]; - if (level_loss < 0) level_loss = 0; - loss = check_exp_loss(op, MIN(level_loss, percentage_loss)); + if (level_loss < 0) + level_loss = 0; + loss = check_exp_loss (op, MIN (level_loss, percentage_loss)); - op->stats.exp -= loss; - player_lvl_adj(op,NULL); + op->stats.exp -= loss; + player_lvl_adj (op, NULL); } /* This function takes an object (monster/player, op), and @@ -1865,12 +1981,14 @@ * resistance to particular attacktype. * Returns 1 if op makes his save, 0 if he failed */ -int did_make_save(const object *op, int level, int bonus) +int +did_make_save (const object *op, int level, int bonus) { - if (level > MAX_SAVE_LEVEL) level = MAX_SAVE_LEVEL; + if (level > MAX_SAVE_LEVEL) + level = MAX_SAVE_LEVEL; + + if ((random_roll (1, 20, op, PREFER_HIGH) + bonus) < savethrow[level]) + return 0; - if ((random_roll(1, 20, op, PREFER_HIGH) + bonus) < savethrow[level]) - return 0; - return 1; + return 1; } -