Implement and use additional "has item" routines

This commit is contained in:
2016-09-01 03:46:59 -04:00
parent 3ca485c54d
commit 23105ba7ed
6 changed files with 144 additions and 115 deletions

View File

@@ -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;
}

View File

@@ -531,12 +531,13 @@ static void display_pc_info(cDialog& me, const short pc) {
else dynamic_cast<cPict&>(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 {

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -622,30 +622,17 @@ bool cPlayer::unequip_item(int which_item, bool do_print) {
return true;
}
auto cPlayer::get_weapons() -> std::pair<decltype(items)::const_iterator, decltype(items)::const_iterator> {
using iter_t = decltype(items)::const_iterator;
iter_t beg = items.begin(), end = items.end();
std::pair<iter_t, iter_t> 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<cInvenSlot, cInvenSlot> 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<typename Fcn>
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<cPlayer*>(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<cPlayer*>(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<cPlayer*>(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<cPlayer*>(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<cPlayer*>(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<cPlayer*>(this)->has_class(item_class);
}
cInvenSlot::operator bool() const {
return slot < owner.items.size();
}

View File

@@ -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<typename Fcn>
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<decltype(items)::const_iterator, decltype(items)::const_iterator>;
std::pair<cInvenSlot, cInvenSlot> 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;