--- deliantra/server/common/living.C 2007/06/04 13:04:00 1.67 +++ deliantra/server/common/living.C 2008/04/21 23:35:24 1.82 @@ -1,29 +1,27 @@ /* - * This file is part of Crossfire TRT, the Multiplayer Online Role Playing Game. + * This file is part of Deliantra, the Roguelike Realtime MMORPG. * - * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team + * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra 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 2 of the License, or (at your option) - * any later version. + * Deliantra 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. + * 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 Crossfire TRT; if not, write to the Free Software Foundation, Inc. 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * 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 + * The authors can be reached via e-mail to */ #include -#include /* Handy little macro that adds exp and keeps it within bounds. Since * we are now using 64 bit values, I'm not all concerned about overflow issues @@ -707,6 +705,46 @@ } } +/* 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. + */ +static struct digest_types : std::bitset +{ + digest_types () + { + set (WEAPON); + set (BOW); + set (ARMOUR); + set (HELMET); + set (SHIELD); + set (RING); + set (BOOTS); + set (GLOVES); + set (AMULET); + set (GIRDLE); + set (BRACERS); + set (CLOAK); + set (DISEASE); + set (FORCE); + set (SKILL); + } +} digest_types; + +static struct copy_flags : object::flags_t +{ + copy_flags () + { + set (FLAG_LIFESAVE); + set (FLAG_REFL_SPELL); + set (FLAG_REFL_MISSILE); + set (FLAG_STEALTH); + set (FLAG_XRAYS); + set (FLAG_BLIND); + set (FLAG_SEE_IN_DARK); + } +} copy_flags; + /* * Updates all abilities given by applied objects in the inventory * of the given object. Note: This function works for both monsters @@ -720,19 +758,19 @@ void object::update_stats () { - int i, j; - float f, max = 9, added_speed = 0, bonus_speed = 0, sp_tmp, speed_reduce_from_disease = 1; + float f, max_speed = 9, added_speed = 0, bonus_speed = 0, 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; + int stat_sum [NUM_STATS]; /* 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); + for (int i = 0; i < NUM_STATS; i++) + stat_sum [i] = contr->orig_stats.stat (i); if (settings.spell_encumbrance == TRUE) contr->encumbrance = 0; @@ -780,7 +818,7 @@ */ memcpy (&resist, &arch->resist, sizeof (resist)); - for (i = 0; i < NROFATTACKS; i++) + for (int i = 0; i < NROFATTACKS; i++) { if (resist[i] > 0) prot[i] = resist[i], vuln[i] = 0; @@ -802,7 +840,7 @@ */ if (!QUERY_FLAG (this, FLAG_USE_ARMOUR) && type == PLAYER) { - ac = MAX (-10, arch->stats.ac - level / 3); + ac = max (-10, arch->stats.ac - level / 3); prot[ATNR_PHYSICAL] += ((100 - prot[AT_PHYSICAL]) * (80 * level / settings.max_level)) / 100; } else @@ -867,6 +905,8 @@ { if (type == PLAYER) { + contr->item_power += tmp->item_power; + if (tmp == contr->combat_ob || tmp == contr->ranged_ob) if (tmp != current_weapon && (tmp->type != SKILL || tmp->subtype != SK_PRAYING) @@ -874,28 +914,16 @@ && !tmp->flag [FLAG_DAMNED]) continue; - for (i = 0; i < NUM_STATS; i++) - change_attr_value (&stats, i, tmp->stats.stat (i)); + for (int i = 0; i < NUM_STATS; i++) + stat_sum [i] = stat_sum [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) + if (digest_types [tmp->type]) { 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; - contr->item_power += tmp->item_power; } } /* if this is a player */ else @@ -906,16 +934,12 @@ /* Update slots used for items */ if (QUERY_FLAG (tmp, FLAG_APPLIED)) - for (i = 0; i < NUM_BODY_LOCATIONS; i++) + for (int 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; - } + speed_reduce_from_disease = + min (speed_reduce_from_disease, tmp->last_sp ? tmp->last_sp / 100.f : 1.f); /* Pos. and neg. protections are counted seperate (-> pro/vuln). * (Negative protections are calculated exactly like positive.) @@ -924,7 +948,7 @@ */ if (tmp->type != POTION) { - for (i = 0; i < NROFATTACKS; i++) + for (int 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. @@ -932,7 +956,7 @@ if (tmp->type == POTION_EFFECT) { if (potion_resist[i]) - potion_resist[i] = MAX (potion_resist[i], tmp->resist[i]); + potion_resist[i] = max (potion_resist[i], tmp->resist[i]); else potion_resist[i] = tmp->resist[i]; } @@ -954,13 +978,7 @@ 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); + flag |= tmp->flag & copy_flags; if (QUERY_FLAG (tmp, FLAG_UNDEAD) && !QUERY_FLAG (arch, FLAG_UNDEAD)) SET_FLAG (this, FLAG_UNDEAD); @@ -1127,8 +1145,8 @@ 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; + if (ARMOUR_SPEED (tmp)) + max_speed = min (max_speed, ARMOUR_SPEED (tmp) / 10.f); break; } /* switch tmp->type */ @@ -1145,7 +1163,7 @@ * 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++) + for (int i = 0; i < NROFATTACKS; i++) { resist[i] = prot[i] - vuln[i]; @@ -1153,9 +1171,15 @@ resist[i] = potion_resist[i]; } - /* Figure out the players sp/mana/hp totals. */ if (type == PLAYER) { + // clamp various player stats + for (int i = 0; i < NUM_STATS; ++i) + stats.stat (i) = clamp (stat_sum [i], MIN_STAT, MAX_STAT); + + contr->digestion = clamp (contr->digestion, MIN_DIGESTION, MAX_DIGESTION); + + /* Figure out the players sp/mana/hp totals. */ int pl_level; check_stat_bounds (&(stats)); @@ -1167,23 +1191,21 @@ /* 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++) + stats.maxhp = 0; + for (int i = 1; i <= min (10, pl_level); i++) { - j = contr->levhp[i] + con_bonus[stats.Con] / 2; + int 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--; - } + if (con_bonus[stats.Con] > 0) + j++; + else + j--; stats.maxhp += j > 1 ? j : 1; /* always get at least 1 hp/level */ } - for (i = 11; i <= level; i++) - stats.maxhp += 2; + stats.maxhp += 2 * max (0, level - 10); if (stats.hp > stats.maxhp) stats.hp = stats.maxhp; @@ -1206,9 +1228,9 @@ stats.maxsp = 1; else { - sp_tmp = 0.f; + float sp_tmp = 0.f; - for (i = 1; i <= mana_obj->level && i <= 10; i++) + for (int i = 1; i <= min (10, mana_obj->level); i++) { float stmp; @@ -1218,21 +1240,14 @@ 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; + sp_tmp += max (1.f, stmp); } - stats.maxsp = (sint16)sp_tmp; - - for (i = 11; i <= mana_obj->level; i++) - stats.maxsp += 2; + stats.maxsp = int (sp_tmp) + 2 * max (0, mana_obj->level - 10); } /* Characters can get their sp supercharged via rune of transferrance */ - if (stats.sp > stats.maxsp * 2) - stats.sp = stats.maxsp * 2; + stats.sp = min (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) @@ -1247,8 +1262,9 @@ * 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 sp_tmp = 0.f; + + for (int i = 1; i <= min (10, grace_obj->level); i++) { float grace_tmp = 0.f; @@ -1258,17 +1274,11 @@ 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; + sp_tmp += max (1.f, 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; + /* two grace points per level after 10 */ + stats.maxgrace = int (sp_tmp) + 2 * max (0, grace_obj->level - 10); } /* No limit on grace vs maxgrace */ @@ -1298,7 +1308,7 @@ { wc -= wc_obj->level + thaco_bonus[stats.Str]; - for (i = 1; i < wc_obj->level; i++) + for (int i = 1; i < wc_obj->level; i++) { /* additional wc every 6 levels */ if (!(i % 6)) @@ -1332,8 +1342,7 @@ speed /= 1.f - added_speed; /* Max is determined by armour */ - if (speed > max) - speed = max; + speed = min (speed, max_speed); if (type == PLAYER) { @@ -1342,20 +1351,22 @@ * 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) + float f = (carrying / 1000) - max_carry[stats.Str]; + if (f > 0.f) speed = speed / (1.f + f / max_carry[stats.Str]); } speed += bonus_speed / 10.f; /* Not affected by limits */ + 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. + * 25 ticks or so. This amounts to once every 3 seconds of realtime. */ - speed = speed * speed_reduce_from_disease; + if (speed < 0.04f && type == PLAYER) + speed = 0.04f; - if (speed < 0.01f && type == PLAYER) - speed = 0.01f; + if (speed != old_speed) + set_speed (speed); if (type == PLAYER) { @@ -1397,9 +1408,6 @@ 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. */ @@ -1469,10 +1477,10 @@ 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 / 25.); /* now set the new title */ - if (pl->contr != NULL) + if (pl->contr) { if (level == 0) sprintf (pl->contr->title, "%s hatchling", attacks[atnr]); @@ -1511,14 +1519,11 @@ char buf[MAX_BUF]; /* tmp. string buffer */ /* now grab the 'dragon_ability'-forces from the player's inventory */ - shstr_cmp dragon_ability_force ("dragon_ability_force"); - shstr_cmp dragon_skin_force ("dragon_skin_force"); - for (tmp = who->inv; tmp; tmp = tmp->below) if (tmp->type == FORCE) - if (tmp->arch->archname == dragon_ability_force) + if (tmp->arch->archname == shstr_dragon_ability_force) abil = tmp; - else if (tmp->arch->archname == dragon_skin_force) + else if (tmp->arch->archname == shstr_dragon_skin_force) skin = tmp; /* if the force is missing -> bail out */ @@ -1604,12 +1609,15 @@ player_lvl_adj (object *who, object *op) { char buf[MAX_BUF]; + bool changed = false; if (!op) /* when rolling stats */ op = who; - if (op->level < settings.max_level && op->stats.exp >= level_exp (op->level + 1, who->expmul)) + while (op->level < settings.max_level && op->stats.exp >= level_exp (op->level + 1, who->expmul)) { + changed = true; + op->level++; if (op && op == who && op->stats.exp > 1 && is_dragon_pl (who)) @@ -1623,37 +1631,44 @@ who->contr->levgrace[who->level] = die_roll (2, 2, who, PREFER_HIGH) - 1; } - 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); + { + who->contr->play_sound (sound_find ("skill_up")); + 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); + { + who->contr->play_sound (sound_find ("level_up")); + 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.exp < level_exp (op->level, who->expmul)) + + while (op->level > 1 && op->stats.exp < level_exp (op->level, who->expmul)) { + changed = true; + 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); + if (changed) + { + who->update_stats (); + esrv_update_stats (who->contr); + /* check if the spell data has changed */ + esrv_update_spells (who->contr); + } } /*