diff --git a/src/boe.combat.cpp b/src/boe.combat.cpp index 98920636..46660bb0 100644 --- a/src/boe.combat.cpp +++ b/src/boe.combat.cpp @@ -503,7 +503,7 @@ bool pc_combat_move(location destination) { void char_parry() { univ.party[current_pc].parry = (univ.party[current_pc].ap / 4) * - (2 + stat_adj(current_pc,eSkill::DEXTERITY) + univ.party[current_pc].skills[eSkill::DEFENSE]); + (2 + stat_adj(current_pc,eSkill::DEXTERITY) + univ.party[current_pc].skill(eSkill::DEFENSE)); univ.party[current_pc].ap = 0; } @@ -514,9 +514,8 @@ void char_stand_ready() { void pc_attack(short who_att,short target) { short r1,r2,weap1 = 24, weap2 = 24,i,store_hp,skill_item; - eSkill what_skill1 = eSkill::DEXTERITY, what_skill2 = eSkill::DEXTERITY; cCreature *which_m; - short hit_adj, dam_adj, spec_dam = 0,poison_amt; + short hit_adj, dam_adj; // slice out bad attacks if(univ.party[who_att].main_status != eMainStatus::ALIVE) @@ -570,7 +569,7 @@ void pc_attack(short who_att,short target) { r1 += 5 * (univ.party[current_pc].status[eStatus::WEBS] / 3); r2 = get_ran(1,1,4) + dam_adj; - if(r1 <= hit_chance[univ.party[who_att].skills[what_skill1]]) { + if(r1 <= hit_chance[univ.party[who_att].skill(eSkill::DEXTERITY)]) { damage_monst(target, who_att, r2, 0,eDamageType::WEAPON,4); } else { @@ -636,7 +635,7 @@ void pc_attack_weapon(short who_att,short target,short hit_adj,short dam_adj,cIt if(weap.ability == eItemAbil::WEAK_WEAPON) r2 = (r2 * (10 - weap.abil_data[0])) / 10; - if(r1 <= hit_chance[univ.party[who_att].skills[what_skill]]) { + if(r1 <= hit_chance[univ.party[who_att].skill(what_skill)]) { eDamageType dmg_tp = eDamageType::UNBLOCKABLE; short spec_dam = calc_spec_dam(weap.ability,weap.abil_data[0],weap.abil_data[1],which_m,dmg_tp); short bonus_dam = 0; @@ -645,10 +644,10 @@ void pc_attack_weapon(short who_att,short target,short hit_adj,short dam_adj,cIt if(primary) { // assassinate r1 = get_ran(1,1,100); - if((univ.party[who_att].level >= which_m->level - 1) - && univ.party[who_att].skills[eSkill::ASSASSINATION] >= which_m->level / 2 + int assassin = univ.party[who_att].skill(eSkill::ASSASSINATION); + if((univ.party[who_att].level >= which_m->level - 1) && assassin >= which_m->level / 2 && (!which_m->abil[eMonstAbil::SPLITS].active)) // Can't assassinate splitters - if(r1 < hit_chance[max(univ.party[who_att].skills[eSkill::ASSASSINATION] - which_m->level,0)]) { + if(r1 < hit_chance[max(assassin - which_m->level,0)]) { add_string_to_buf(" You assassinate."); spec_dam += r2; } @@ -1481,7 +1480,7 @@ void fire_missile(location target) { bool exploding = false; missile_firer = current_pc; - skill = univ.party[missile_firer].skills[univ.party[missile_firer].items[missile_inv_slot].weap_type]; + skill = univ.party[missile_firer].skill(univ.party[missile_firer].items[missile_inv_slot].weap_type); range = (overall_mode == MODE_FIRING) ? 12 : 8; dam = univ.party[missile_firer].items[ammo_inv_slot].item_level; dam_bonus = univ.party[missile_firer].items[ammo_inv_slot].bonus + minmax(-8,8,univ.party[missile_firer].status[eStatus::BLESS_CURSE]); @@ -4297,7 +4296,7 @@ bool combat_cast_mage_spell() { store_sp = univ.party[current_pc].cur_sp; if(univ.party[current_pc].cur_sp == 0) add_string_to_buf("Cast: No spell points. "); - else if(univ.party[current_pc].skills[eSkill::MAGE_SPELLS] == 0) + else if(univ.party[current_pc].skill(eSkill::MAGE_SPELLS) == 0) add_string_to_buf("Cast: No mage skill. "); else if(get_encumberance(current_pc) > 1) { add_string_to_buf("Cast: Too encumbered. "); @@ -4508,7 +4507,7 @@ bool combat_cast_priest_spell() { if(univ.party[current_pc].cur_sp == 0) { add_string_to_buf("Cast: No spell points. "); return false; - } else if(univ.party[current_pc].skills[eSkill::PRIEST_SPELLS] == 0) { + } else if(univ.party[current_pc].skill(eSkill::PRIEST_SPELLS) == 0) { add_string_to_buf("Cast: No priest skill. "); return false; } @@ -4798,8 +4797,8 @@ static void process_force_cage(location loc, short i) { cPlayer& who = univ.party[i]; // We want to make sure everyone has a chance of eventually breaking a cage, because it never ends on its own, // and because being trapped unconditionally prevents you from ending combat mode. - short bonus = 5 + who.skills[eSkill::MAGE_LORE]; - if(get_ran(1,1,100) < who.skills[eSkill::MAGE_SPELLS]*10 + who.skills[eSkill::PRIEST_SPELLS]*4 + bonus) { + short bonus = 5 + who.skill(eSkill::MAGE_LORE); + if(get_ran(1,1,100) < who.skill(eSkill::MAGE_SPELLS)*10 + who.skill(eSkill::PRIEST_SPELLS)*4 + bonus) { play_sound(60); add_string_to_buf(" " + who.name + " breaks force cage."); univ.town.set_force_cage(loc.x,loc.y,false); diff --git a/src/boe.infodlg.cpp b/src/boe.infodlg.cpp index c2f82813..2bb51a8e 100644 --- a/src/boe.infodlg.cpp +++ b/src/boe.infodlg.cpp @@ -541,7 +541,11 @@ static void display_pc_info(cDialog& me, const short pc) { for(i = 0; i < 19; i++) { eSkill skill = eSkill(i); - me[skill_ids[i]].setTextToNum(univ.party[pc].skills[skill]); + int bonus = univ.party[pc].get_prot_level(eItemAbil::BOOST_STAT, i); + to_draw << univ.party[pc].skills[skill]; + if(bonus > 0) to_draw << '+' << bonus; + me[skill_ids[i]].setText(to_draw.str()); + to_draw.str(""); } store = total_encumberance(pc); me["encumb"].setTextToNum(store); diff --git a/src/boe.items.cpp b/src/boe.items.cpp index b49cafe8..09928183 100644 --- a/src/boe.items.cpp +++ b/src/boe.items.cpp @@ -996,7 +996,7 @@ void place_treasure(location where,short level,short loot,short mode) { if(new_item.variety != eItemType::NO_ITEM) { for(i = 0; i < 6; i++) if((univ.party[i].main_status == eMainStatus::ALIVE) - && get_ran(1,1,100) < id_odds[univ.party[i].skills[eSkill::ITEM_LORE]]) + && get_ran(1,1,100) < id_odds[univ.party[i].skill(eSkill::ITEM_LORE)]) new_item.ident = true; place_item(new_item,where,false); } diff --git a/src/boe.monster.cpp b/src/boe.monster.cpp index 8320f27a..d5ec8e0e 100644 --- a/src/boe.monster.cpp +++ b/src/boe.monster.cpp @@ -1378,9 +1378,9 @@ short get_encumberance(short pc_num) { for(i = 0; i < 24; i++) if(univ.party[pc_num].equip[i]) { what_val = univ.party[pc_num].items[i].awkward; - if(what_val == 1 && get_ran(1,0,130) < hit_chance[univ.party[pc_num].skills[eSkill::DEFENSE]]) + if(what_val == 1 && get_ran(1,0,130) < hit_chance[univ.party[pc_num].skill(eSkill::DEFENSE)]) what_val--; - if(what_val > 1 && get_ran(1,0,70) < hit_chance[univ.party[pc_num].skills[eSkill::DEFENSE]]) + if(what_val > 1 && get_ran(1,0,70) < hit_chance[univ.party[pc_num].skill(eSkill::DEFENSE)]) what_val--; store += what_val; } diff --git a/src/boe.party.cpp b/src/boe.party.cpp index c20c2ec4..c90bfe55 100644 --- a/src/boe.party.cpp +++ b/src/boe.party.cpp @@ -648,8 +648,8 @@ void award_xp(short pc_num,short amt) { std::string level = std::to_string(univ.party[pc_num].level); add_string_to_buf(" " + univ.party[pc_num].name + " is level " + level + "!"); univ.party[pc_num].skill_pts += (univ.party[pc_num].level < 20) ? 5 : 4; - add_hp = (univ.party[pc_num].level < 26) ? get_ran(1,2,6) + skill_bonus[univ.party[pc_num].skills[eSkill::STRENGTH]] - : max (skill_bonus[univ.party[pc_num].skills[eSkill::STRENGTH]],0); + add_hp = (univ.party[pc_num].level < 26) ? get_ran(1,2,6) + skill_bonus[univ.party[pc_num].skill(eSkill::STRENGTH)] + : max (skill_bonus[univ.party[pc_num].skill(eSkill::STRENGTH)],0); if(add_hp < 0) add_hp = 0; univ.party[pc_num].max_health += add_hp; @@ -687,7 +687,7 @@ static short check_party_stat_get(short pc, eSkill which_stat) { case eSkill::CUR_LEVEL: return univ.party[pc].level; default: - return univ.party[pc].skills[which_stat]; + return univ.party[pc].skill(which_stat); } return 0; } @@ -739,11 +739,11 @@ bool poison_weapon( short pc_num, short how_much,short safe) { // Nimble? if(univ.party[pc_num].traits[eTrait::NIMBLE]) r1 -= 6; - if((r1 > p_chance[univ.party[pc_num].skills[eSkill::POISON]]) && (safe == 0)) { + if((r1 > p_chance[univ.party[pc_num].skill(eSkill::POISON)]) && (safe == 0)) { add_string_to_buf(" Poison put on badly. "); p_level = p_level / 2; r1 = get_ran(1,1,100); - if(r1 > p_chance[univ.party[pc_num].skills[eSkill::POISON]] + 10) { + if(r1 > p_chance[univ.party[pc_num].skill(eSkill::POISON)] + 10) { add_string_to_buf(" You nick yourself. "); univ.party[pc_num].status[eStatus::POISON] += p_level; } @@ -1629,7 +1629,7 @@ void crumble_wall(location where) { // TODO: Add something like this to the spre void do_mindduel(short pc_num,cCreature *monst) { short i = 0,adjust,r1,r2,balance = 0; - adjust = (univ.party[pc_num].level + univ.party[pc_num].skills[eSkill::INTELLIGENCE]) / 2 - monst->level * 2; + adjust = (univ.party[pc_num].level + univ.party[pc_num].skill(eSkill::INTELLIGENCE)) / 2 - monst->level * 2; adjust += univ.party[pc_num].get_prot_level(eItemAbil::WILL) * 5; if(monst->attitude % 2 != 1) make_town_hostile(); @@ -1757,7 +1757,7 @@ bool pc_can_cast_spell(short pc_num,eSpell spell_num) { return false; // From Windows version. It does kinda make sense, though this function shouldn't even be called in these modes. if(!isMage(spell_num) && !isPriest(spell_num)) return false; - if(univ.party[pc_num].skills[type] < level) + if(univ.party[pc_num].skill(type) < level) return false; if(univ.party[pc_num].main_status != eMainStatus::ALIVE) return false; @@ -2180,7 +2180,7 @@ eSpell pick_spell(short pc_num,eSkill type) { // 70 - no spell OW spell num } if(!can_choose_caster) { - if(univ.party[pc_num].skills[type] == 0) { + if(univ.party[pc_num].skill(type) == 0) { if(type == eSkill::MAGE_SPELLS) add_string_to_buf("Cast: No mage skill."); else add_string_to_buf("Cast: No priest skill."); return eSpell::NONE; @@ -2277,6 +2277,8 @@ void print_spell_cast(eSpell spell,eSkill which) { short stat_adj(short pc_num,eSkill which) { short tr; + // This is one place where we use the base skill level instead of the adjusted skill level + // Using the adjusted skill level here would alter the original mechanics of stat-boosting items tr = skill_bonus[univ.party[pc_num].skills[which]]; if(which == eSkill::INTELLIGENCE) { if(univ.party[pc_num].traits[eTrait::MAGICALLY_APT]) @@ -2366,16 +2368,16 @@ void do_alchemy() { play_sound(8); r1 = get_ran(1,1,100); - if(r1 < fail_chance[univ.party[pc_num].skills[eSkill::ALCHEMY] - alch_difficulty[which_p]]) { + if(r1 < fail_chance[univ.party[pc_num].skill(eSkill::ALCHEMY) - alch_difficulty[which_p]]) { add_string_to_buf("Alchemy: Failed. "); r1 = get_ran(1,0,1); play_sound(41 ); } else { cItem store_i(potion); - if(univ.party[pc_num].skills[eSkill::ALCHEMY] - alch_difficulty[which_p] >= 5) + if(univ.party[pc_num].skill(eSkill::ALCHEMY) - alch_difficulty[which_p] >= 5) store_i.charges++; - if(univ.party[pc_num].skills[eSkill::ALCHEMY] - alch_difficulty[which_p] >= 11) + if(univ.party[pc_num].skill(eSkill::ALCHEMY) - alch_difficulty[which_p] >= 11) store_i.charges++; store_i.graphic_num += get_ran(1,0,2); if(!univ.party[pc_num].give_item(store_i,false)) { @@ -2420,12 +2422,12 @@ eAlchemy alch_choice(short pc_num) { std::string n = boost::lexical_cast(i + 1); chooseAlchemy["label" + n].setText(get_str("magic-names", i + 200)); chooseAlchemy["potion" + n].attachClickHandler(alch_choice_event_filter); - if(univ.party[pc_num].skills[eSkill::ALCHEMY] < difficulty[i] || univ.party.alchemy[i] == 0) + if(univ.party[pc_num].skill(eSkill::ALCHEMY) < difficulty[i] || univ.party.alchemy[i] == 0) chooseAlchemy["potion" + n].hide(); } std::ostringstream sout; sout << univ.party[pc_num].name; - sout << " (skill " << univ.party[pc_num].skills[eSkill::ALCHEMY] << ")"; + sout << " (skill " << univ.party[pc_num].skill(eSkill::ALCHEMY) << ")"; chooseAlchemy["mixer"].setText(sout.str()); if(univ.party.help_received[20] == 0) { // TODO: I'm not sure if the initial draw is needed @@ -2624,7 +2626,7 @@ bool damage_pc(short which_pc,short how_much,eDamageType damage_type,eRace type_ how_much = how_much - univ.party[which_pc].items[i].bonus; } r1 = get_ran(1,1,100); - if(r1 < hit_chance[univ.party[which_pc].skills[eSkill::DEFENSE]] - 20) + if(r1 < hit_chance[univ.party[which_pc].skill(eSkill::DEFENSE)] - 20) how_much -= 1; } if(univ.party[which_pc].items[i].protection > 0) { @@ -2651,7 +2653,7 @@ bool damage_pc(short which_pc,short how_much,eDamageType damage_type,eRace type_ if(univ.party[which_pc].traits[eTrait::TOUGHNESS]) how_much--; // luck - if(get_ran(1,1,100) < 2 * (hit_chance[univ.party[which_pc].skills[eSkill::LUCK]] - 20)) + if(get_ran(1,1,100) < 2 * (hit_chance[univ.party[which_pc].skill(eSkill::LUCK)] - 20)) how_much -= 1; } @@ -2772,8 +2774,9 @@ void kill_pc(short which_pc,eMainStatus type) { if(type != eMainStatus::STONE) i = univ.party[which_pc].has_abil_equip(eItemAbil::LIFE_SAVING); - if(!no_save && type != eMainStatus::ABSENT && univ.party[which_pc].skills[eSkill::LUCK] > 0 && - get_ran(1,1,100) < hit_chance[univ.party[which_pc].skills[eSkill::LUCK]]) { + int luck = univ.party[which_pc].skill(eSkill::LUCK); + if(!no_save && type != eMainStatus::ABSENT && luck > 0 && + get_ran(1,1,100) < hit_chance[luck]) { add_string_to_buf(" But you luck out! "); univ.party[which_pc].cur_health = 0; } diff --git a/src/boe.town.cpp b/src/boe.town.cpp index 78ebf9df..5166f8d4 100644 --- a/src/boe.town.cpp +++ b/src/boe.town.cpp @@ -1073,7 +1073,7 @@ void pick_lock(location where,short pc_num) { will_break = true; r1 = get_ran(1,1,100) - 5 * stat_adj(pc_num,eSkill::DEXTERITY) + univ.town.difficulty * 7 - - 5 * univ.party[pc_num].skills[eSkill::LOCKPICKING] - univ.party[pc_num].items[which_item].abil_data[0] * 7; + - 5 * univ.party[pc_num].skill(eSkill::LOCKPICKING) - univ.party[pc_num].items[which_item].abil_data[0] * 7; // Nimble? if(univ.party[pc_num].traits[eTrait::NIMBLE]) diff --git a/src/boe.townspec.cpp b/src/boe.townspec.cpp index dd28eeb1..69a0d519 100644 --- a/src/boe.townspec.cpp +++ b/src/boe.townspec.cpp @@ -66,8 +66,8 @@ bool run_trap(short pc_num,eTrapType trap_type,short trap_level,short diff) { if(pc_num < 6) { i = stat_adj(pc_num,eSkill::DEXTERITY); i += univ.party[pc_num].get_prot_level(eItemAbil::THIEVING) / 2; - skill = minmax(0,20,univ.party[pc_num].skills[eSkill::DISARM_TRAPS] + - + univ.party[pc_num].skills[eSkill::LUCK] / 2 + 1 - univ.town.difficulty + 2 * i); + skill = minmax(0,20,univ.party[pc_num].skill(eSkill::DISARM_TRAPS) + + + univ.party[pc_num].skill(eSkill::LUCK) / 2 + 1 - univ.town.difficulty + 2 * i); r1 = get_ran(1,1,100) + diff; // Nimble? diff --git a/src/classes/pc.cpp b/src/classes/pc.cpp index a6c14c95..1d0e5d74 100644 --- a/src/classes/pc.cpp +++ b/src/classes/pc.cpp @@ -186,7 +186,7 @@ bool cPlayer::give_item(cItem item, bool do_print, bool allow_overload) { } short cPlayer::max_weight() { - return 100 + (15 * min(skills[eSkill::STRENGTH],20)) + (traits[eTrait::STRENGTH] * 30) + return 100 + (15 * min(skill(eSkill::STRENGTH),20)) + (traits[eTrait::STRENGTH] * 30) + (traits[eTrait::BAD_BACK] * -50); } @@ -281,6 +281,10 @@ short cPlayer::has_abil(eItemAbil abil,short dat) { return 24; } +short cPlayer::skill(eSkill skill) { + return min(20, skills[skill] + get_prot_level(eItemAbil::BOOST_STAT, int(skill))); +} + eBuyStatus cPlayer::ok_to_buy(short cost,cItem item) { if(item.variety != eItemType::GOLD && item.variety != eItemType::FOOD) { for(int i = 0; i < 24; i++) diff --git a/src/classes/pc.h b/src/classes/pc.h index a62f354c..45fa14cd 100644 --- a/src/classes/pc.h +++ b/src/classes/pc.h @@ -69,6 +69,7 @@ public: short get_prot_level(eItemAbil abil, short dat = -1); short has_abil_equip(eItemAbil abil, short dat = -1); short has_abil(eItemAbil abil, short dat = -1); + short skill(eSkill skill); eBuyStatus ok_to_buy(short cost,cItem item); void append(legacy::pc_record_type old);