Implement and use additional "has item" routines
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user