From 23105ba7edbc1a2f38e292b74f6485dabdd02a78 Mon Sep 17 00:00:00 2001 From: Celtic Minstrel Date: Thu, 1 Sep 2016 03:46:59 -0400 Subject: [PATCH] Implement and use additional "has item" routines --- src/boe.combat.cpp | 103 ++++++++++++++++----------------------- src/boe.infodlg.cpp | 11 +++-- src/boe.specials.cpp | 6 +-- src/classes/party.cpp | 9 ++-- src/classes/pc.cpp | 109 +++++++++++++++++++++++++++--------------- src/classes/pc.hpp | 21 +++++++- 6 files changed, 144 insertions(+), 115 deletions(-) diff --git a/src/boe.combat.cpp b/src/boe.combat.cpp index 832cdd3c0..6ba7667b2 100644 --- a/src/boe.combat.cpp +++ b/src/boe.combat.cpp @@ -529,7 +529,8 @@ void pc_attack(short who_att,iLiving* target) { attacker.last_attacked = target; auto weapons = attacker.get_weapons(); - auto weap1 = weapons.first, weap2 = weapons.second, no_weap = attacker.items.cend(); + auto& weap1 = weapons.first; + auto& weap2 = weapons.second; hit_adj = (-5 * minmax(-8,8,attacker.status[eStatus::BLESS_CURSE])) + 5 * minmax(-8,8,target->status[eStatus::BLESS_CURSE]) - attacker.stat_adj(eSkill::DEXTERITY) * 5 + (get_encumbrance(who_att)) * 5; @@ -558,7 +559,7 @@ void pc_attack(short who_att,iLiving* target) { combat_posing_monster = current_working_monster = who_att; - if(weap1 == no_weap) { + if(!weap1) { std::string create_line = univ.party[who_att].name + " punches."; add_string_to_buf(create_line); @@ -600,11 +601,10 @@ void pc_attack(short who_att,iLiving* target) { } } - auto weap_poisoned = attacker.items.begin() + attacker.weap_poisoned.slot; - if(weap1 != no_weap) - pc_attack_weapon(who_att, *target, hit_adj, dam_adj, *weap1, 1 + (weap2 != no_weap), weap_poisoned == weap1); - if(weap2 != no_weap && target->is_alive()) - pc_attack_weapon(who_att, *target, hit_adj, dam_adj, *weap2, 0, weap_poisoned == weap2); + if(weap1) + pc_attack_weapon(who_att, *target, hit_adj, dam_adj, *weap1, 1 + bool(weap2), attacker.weap_poisoned == weap1); + if(weap2 && target->is_alive()) + pc_attack_weapon(who_att, *target, hit_adj, dam_adj, *weap2, 0, attacker.weap_poisoned == weap2); move_to_zero(attacker.status[eStatus::POISONED_WEAPON]); take_ap(4); @@ -1614,92 +1614,71 @@ void handle_marked_damage() { } void load_missile() { - short bow = 24,arrow = 24,thrown = 24,crossbow = 24,bolts = 24,no_ammo = 24; - if(univ.current_pc().traits[eTrait::PACIFIST]) { add_string_to_buf("Shoot: You're a pacifist!"); return; } - for(short i = 0; i < 24; i++) { - if((univ.current_pc().equip[i]) && - (univ.current_pc().items[i].variety == eItemType::THROWN_MISSILE)) - thrown = i; - if((univ.current_pc().equip[i]) && - (univ.current_pc().items[i].variety == eItemType::BOW)) - bow = i; - if((univ.current_pc().equip[i]) && - (univ.current_pc().items[i].variety == eItemType::ARROW)) - arrow = i; - if((univ.current_pc().equip[i]) && - (univ.current_pc().items[i].variety == eItemType::CROSSBOW)) - crossbow = i; - if((univ.current_pc().equip[i]) && - (univ.current_pc().items[i].variety == eItemType::BOLTS)) - bolts = i; - if((univ.current_pc().equip[i]) && - (univ.current_pc().items[i].variety == eItemType::MISSILE_NO_AMMO)) - no_ammo = i; - } + cInvenSlot + thrown = univ.current_pc().has_type_equip(eItemType::THROWN_MISSILE), + bow = univ.current_pc().has_type_equip(eItemType::BOW), + arrow = univ.current_pc().has_type_equip(eItemType::ARROW), + crossbow = univ.current_pc().has_type_equip(eItemType::CROSSBOW), + bolts = univ.current_pc().has_type_equip(eItemType::BOLTS), + no_ammo = univ.current_pc().has_type_equip(eItemType::MISSILE_NO_AMMO); - if(thrown < 24) { - missile_inv_slot = thrown; - ammo_inv_slot = thrown; + if(thrown) { + missile_inv_slot = thrown.slot; + ammo_inv_slot = thrown.slot; add_string_to_buf("Throw: Select a target."); add_string_to_buf(" (Hit 's' to cancel.)"); overall_mode = MODE_THROWING; current_spell_range = 8; - if(univ.current_pc().items[thrown].ability == eItemAbil::DISTANCE_MISSILE) - current_spell_range += univ.current_pc().items[thrown].abil_data[0]; - if(univ.current_pc().items[thrown].ability == eItemAbil::EXPLODING_WEAPON) + if(thrown->ability == eItemAbil::DISTANCE_MISSILE) + current_spell_range += thrown->abil_data[0]; + if(thrown->ability == eItemAbil::EXPLODING_WEAPON) current_pat = radius2; else current_pat = single; - } - else if(((bolts < 24) && (bow < 24)) || ((arrow < 24) && (crossbow < 24))) { + } else if((bow && bolts) || (crossbow && arrow)) { add_string_to_buf("Fire: Wrong ammunition."); - } - else if((arrow == 24) && (bow < 24)) { + } else if(bow && !arrow) { add_string_to_buf("Fire: Equip some arrows."); - } - else if(crossbow == 24 && bolts < 24) { + } else if(crossbow && !bolts) { add_string_to_buf("Fire: Equip some bolts."); - } - else if((arrow < 24) && (bow < 24)) { - missile_inv_slot = bow; - ammo_inv_slot = arrow; + } else if(bow && arrow) { + missile_inv_slot = bow.slot; + ammo_inv_slot = arrow.slot; overall_mode = MODE_FIRING; add_string_to_buf("Fire: Select a target."); add_string_to_buf(" (Hit 's' to cancel.)"); current_spell_range = 12; - if(univ.current_pc().items[arrow].ability == eItemAbil::DISTANCE_MISSILE) - current_spell_range += univ.current_pc().items[arrow].abil_data[0]; - if(univ.current_pc().items[arrow].ability == eItemAbil::EXPLODING_WEAPON) + if(arrow->ability == eItemAbil::DISTANCE_MISSILE) + current_spell_range += arrow->abil_data[0]; + if(arrow->ability == eItemAbil::EXPLODING_WEAPON) current_pat = radius2; else current_pat = single; - } - else if((bolts < 24) && (crossbow < 24)) { - missile_inv_slot = crossbow; - ammo_inv_slot = bolts; + } else if(crossbow && bolts) { + missile_inv_slot = crossbow.slot; + ammo_inv_slot = bolts.slot; overall_mode = MODE_FIRING; add_string_to_buf("Fire: Select a target."); add_string_to_buf(" (Hit 's' to cancel.)"); current_spell_range = 12; - if(univ.current_pc().items[bolts].ability == eItemAbil::DISTANCE_MISSILE) - current_spell_range += univ.current_pc().items[bolts].abil_data[0]; - if(univ.current_pc().items[bolts].ability == eItemAbil::EXPLODING_WEAPON) + if(bolts->ability == eItemAbil::DISTANCE_MISSILE) + current_spell_range += bolts->abil_data[0]; + if(bolts->ability == eItemAbil::EXPLODING_WEAPON) current_pat = radius2; else current_pat = single; - } - else if(no_ammo < 24) { - missile_inv_slot = no_ammo; - ammo_inv_slot = no_ammo; + } else if(no_ammo) { + missile_inv_slot = no_ammo.slot; + ammo_inv_slot = no_ammo.slot; overall_mode = MODE_FIRING; add_string_to_buf("Fire: Select a target."); add_string_to_buf(" (Hit 's' to cancel.)"); current_spell_range = 12; - if(univ.current_pc().items[no_ammo].ability == eItemAbil::DISTANCE_MISSILE) - current_spell_range += univ.current_pc().items[no_ammo].abil_data[0]; - if(univ.current_pc().items[no_ammo].ability == eItemAbil::EXPLODING_WEAPON) + if(no_ammo->ability == eItemAbil::DISTANCE_MISSILE) + current_spell_range += no_ammo->abil_data[0]; + if(no_ammo->ability == eItemAbil::EXPLODING_WEAPON) current_pat = radius2; else current_pat = single; } diff --git a/src/boe.infodlg.cpp b/src/boe.infodlg.cpp index 589e01cde..051847426 100644 --- a/src/boe.infodlg.cpp +++ b/src/boe.infodlg.cpp @@ -531,12 +531,13 @@ static void display_pc_info(cDialog& me, const short pc) { else dynamic_cast(me["pic"]).setPict(pic,PIC_PC); // Fight bonuses - decltype(univ.party[pc].items)::const_iterator weap1, weap2, no_weap = univ.party[pc].items.end(); - std::tie(weap1, weap2) = univ.party[pc].get_weapons(); + auto weapons = univ.party[pc].get_weapons(); + auto& weap1 = weapons.first; + auto& weap2 = weapons.second; hit_adj = univ.party[pc].stat_adj(eSkill::DEXTERITY) * 5 - (total_encumbrance(pc)) * 5 + 5 * minmax(-8,8,univ.party[pc].status[eStatus::BLESS_CURSE]); - if(!univ.party[pc].traits[eTrait::AMBIDEXTROUS] && weap2 != no_weap) + if(!univ.party[pc].traits[eTrait::AMBIDEXTROUS] && weap2) hit_adj -= 25; // TODO: Perhaps dam_adj and hit_adj calculation should be moved into a function somewhere? @@ -554,7 +555,7 @@ static void display_pc_info(cDialog& me, const short pc) { me["weap1b"].setText(""); me["weap2a"].setText("No weapon."); me["weap2b"].setText(""); - if(weap1 != no_weap) { + if(weap1) { if(!weap1->ident) me["weap1a"].setText("Not identified."); else { @@ -569,7 +570,7 @@ static void display_pc_info(cDialog& me, const short pc) { to_draw.str(""); } } - if(weap2 != no_weap) { + if(weap2) { if(!weap2->ident) me["weap2a"].setText("Not identified."); else { diff --git a/src/boe.specials.cpp b/src/boe.specials.cpp index cfbfe1d8b..7053588c1 100644 --- a/src/boe.specials.cpp +++ b/src/boe.specials.cpp @@ -3408,13 +3408,11 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, case eSpecType::IF_EQUIP_ITEM_CLASS: for(cPlayer& pc : univ.party) if(pc.main_status == eMainStatus::ALIVE) - for(short j = 0; j < pc.items.size(); j++) - if(pc.items[j].variety != eItemType::NO_ITEM && pc.items[j].special_class == (unsigned)spec.ex1a - && pc.equip[j]) { + if(cInvenSlot item = pc.has_class_equip(spec.ex1a)) { *next_spec = spec.ex1b; if(spec.ex2a > 0) { *redraw = 1; - pc.take_item(j); + pc.take_item(item.slot); if(&pc == &univ.party[stat_window]) put_item_screen(stat_window); } diff --git a/src/classes/party.cpp b/src/classes/party.cpp index 8f9f11148..8bb68cf64 100644 --- a/src/classes/party.cpp +++ b/src/classes/party.cpp @@ -505,12 +505,11 @@ bool cParty::check_class(unsigned int item_class,bool take) { return false; for(auto& pc : *this) if(pc.main_status == eMainStatus::ALIVE) - for(short i = 0; i < pc.items.size(); i++) - if(pc.items[i].variety != eItemType::NO_ITEM && pc.items[i].special_class == item_class) { + if(cInvenSlot item = pc.has_class(item_class)) { if(take) { - if(pc.items[i].charges > 1) - pc.items[i].charges--; - else pc.take_item(i); + if(item->charges > 1) + item->charges--; + else pc.take_item(item.slot); } return true; } diff --git a/src/classes/pc.cpp b/src/classes/pc.cpp index cf6727075..e71fcef2b 100644 --- a/src/classes/pc.cpp +++ b/src/classes/pc.cpp @@ -622,30 +622,17 @@ bool cPlayer::unequip_item(int which_item, bool do_print) { return true; } -auto cPlayer::get_weapons() -> std::pair { - using iter_t = decltype(items)::const_iterator; - iter_t beg = items.begin(), end = items.end(); - std::pair result = {beg, beg}; - auto is_weapon = [](const cItem& item) { - return item.variety == eItemType::ONE_HANDED || item.variety == eItemType::TWO_HANDED; - }; - auto is_equipped = [this](iter_t item) { - if(item == items.end()) return true; // Treat a non-existent item as equipped to avoid incrementing an end iterator - return equip[item - items.begin()]; - }; - - do { - result.first = std::find_if(result.first, end, is_weapon); - if(!is_equipped(result.first)) result.first++; - } while(result.first != end); - if(result.first != end && result.first + 1 != end) { - result.second = result.first + 1; - do { - result.second = std::find_if(result.second, end, is_weapon); - if(!is_equipped(result.second)) result.second++; - } while(result.second != end); +std::pair cPlayer::get_weapons() { + cInvenSlot weap1 = has_type_equip(eItemType::ONE_HANDED); + if(weap1) { + equip[weap1.slot] = false; + cInvenSlot weap2 = has_type_equip(eItemType::ONE_HANDED); + equip[weap1.slot] = true; + return std::make_pair(weap1, weap2); + } else { + cInvenSlot weap2 = has_type_equip(eItemType::TWO_HANDED); + return std::make_pair(weap2, weap1); } - return result; } short cPlayer::max_weight() const { @@ -726,35 +713,81 @@ short cPlayer::get_prot_level(eItemAbil abil, short dat) const { return sum; // TODO: Should we return -1 if the sum is 0? } -cInvenSlot cPlayer::has_abil_equip(eItemAbil abil,short dat) { - for(short i = 0; i < items.size(); i++) { - if(items[i].variety == eItemType::NO_ITEM) continue; - if(items[i].ability != abil) continue; - if(!equip[i]) continue; - if(dat >= 0 && dat != items[i].abil_data[1]) continue; - return cInvenSlot(*this, i); - } +template +cInvenSlot cPlayer::find_item_matching(Fcn fcn) { + for(short i = 0; i < items.size(); i++) + if(items[i].variety != eItemType::NO_ITEM && fcn(i, items[i])) + return cInvenSlot(*this, i); return cInvenSlot(*this); } +cInvenSlot cPlayer::has_abil_equip(eItemAbil abil,short dat) { + return find_item_matching([this,abil,dat](int i, const cItem& item) { + if(item.variety == eItemType::NO_ITEM) return false; + if(item.ability != abil) return false; + if(!equip[i]) return false; + if(dat >= 0 && dat != item.abil_data[1]) return false; + return true; + }); +} + const cInvenSlot cPlayer::has_abil_equip(eItemAbil abil,short dat) const { return const_cast(this)->has_abil_equip(abil,dat); } cInvenSlot cPlayer::has_abil(eItemAbil abil,short dat) { - for(short i = 0; i < items.size(); i++) { - if(items[i].variety == eItemType::NO_ITEM) continue; - if(items[i].ability != abil) continue; - if(dat >= 0 && dat != items[i].abil_data[1]) continue; - return cInvenSlot(*this, i); - } - return cInvenSlot(*this); + return find_item_matching([this,abil,dat](int, const cItem& item) { + if(item.variety == eItemType::NO_ITEM) return false; + if(item.ability != abil) return false; + if(dat >= 0 && dat != item.abil_data[1]) return false; + return true; + }); } const cInvenSlot cPlayer::has_abil(eItemAbil abil,short dat) const { return const_cast(this)->has_abil(abil,dat); } +cInvenSlot cPlayer::has_type_equip(eItemType type) { + return find_item_matching([this,type](int i, const cItem& item) { + return equip[i] && item.variety == type; + }); +} + +const cInvenSlot cPlayer::has_type_equip(eItemType type) const { + return const_cast(this)->has_type_equip(type); +} + +cInvenSlot cPlayer::has_type(eItemType type) { + return find_item_matching([type](int, const cItem& item) { + return item.variety == type; + }); +} + +const cInvenSlot cPlayer::has_type(eItemType type) const { + return const_cast(this)->has_type(type); +} + +cInvenSlot cPlayer::has_class_equip(unsigned int item_class) { + return find_item_matching([this,item_class](int i, const cItem& item) { + return equip[i] && item.special_class == item_class; + }); +} + +const cInvenSlot cPlayer::has_class_equip(unsigned int item_class) const { + return const_cast(this)->has_class_equip(item_class); +} + +cInvenSlot cPlayer::has_class(unsigned int item_class) { + return find_item_matching([item_class](int, const cItem& item) { + return item.special_class == item_class; + }); +} + +const cInvenSlot cPlayer::has_class(unsigned int item_class) const { + return const_cast(this)->has_class(item_class); +} + cInvenSlot::operator bool() const { return slot < owner.items.size(); } diff --git a/src/classes/pc.hpp b/src/classes/pc.hpp index 99b4fc287..1229b0bd1 100644 --- a/src/classes/pc.hpp +++ b/src/classes/pc.hpp @@ -45,12 +45,21 @@ struct cInvenSlot { const cItem* operator->() const; cItem& operator*(); const cItem& operator*() const; + friend bool operator==(const cInvenSlot& a, const cInvenSlot& b) { + return &a.owner == &b.owner && a.slot == b.slot; + } private: cPlayer& owner; }; +inline bool operator!=(const cInvenSlot& a, const cInvenSlot& b) { + return !(a == b); +} + class cPlayer : public iLiving { cParty* party; + template + cInvenSlot find_item_matching(Fcn fcn); public: static void(* give_help)(short,short); eMainStatus main_status; @@ -113,7 +122,7 @@ public: bool give_item(cItem item, int flags); bool equip_item(int which_item, bool do_print); bool unequip_item(int which_item, bool do_print); - auto get_weapons() -> std::pair; + std::pair get_weapons(); void take_item(int which_item); void remove_charge(int which_item); const cInvenSlot has_space() const; @@ -122,10 +131,20 @@ public: short cur_weight() const; short free_weight() const; short get_prot_level(eItemAbil abil, short dat = -1) const; + const cInvenSlot has_abil_equip(eItemAbil abil, short dat = -1) const; cInvenSlot has_abil_equip(eItemAbil abil, short dat = -1); const cInvenSlot has_abil(eItemAbil abil, short dat = -1) const; cInvenSlot has_abil(eItemAbil abil, short dat = -1); + const cInvenSlot has_type_equip(eItemType type) const; + cInvenSlot has_type_equip(eItemType type); + const cInvenSlot has_type(eItemType type) const; + cInvenSlot has_type(eItemType type); + const cInvenSlot has_class_equip(unsigned int item_class) const; + cInvenSlot has_class_equip(unsigned int item_class); + const cInvenSlot has_class(unsigned int item_class) const; + cInvenSlot has_class(unsigned int item_class); + short skill(eSkill skill) const; short stat_adj(eSkill skill) const; eBuyStatus ok_to_buy(short cost,cItem item) const;