Allow PC to accept item that will stack, even if slots full

This commit is contained in:
2025-03-19 17:50:24 -05:00
parent cb73d5d7f6
commit 599f1030f2
7 changed files with 62 additions and 38 deletions

View File

@@ -373,7 +373,7 @@ void handle_sale(int i) {
univ.current_pc().status[eStatus::PARALYZED] = 0;
break;
case eShopItemType::REMOVE_CURSE:
for(int i = 0; i < univ.current_pc().items.size(); i++)
for(int i = 0; i < cPlayer::INVENTORY_SIZE; i++)
if((univ.current_pc().equip[i]) &&
(univ.current_pc().items[i].cursed))
univ.current_pc().items[i].cursed = univ.current_pc().items[i].unsellable = false;
@@ -589,7 +589,7 @@ void set_up_shop_array() {
shop_array.push_back(j);
break;
case eShopItemType::REMOVE_CURSE:
for(int i = 0; i < univ.current_pc().items.size(); i++) {
for(int i = 0; i < cPlayer::INVENTORY_SIZE; i++) {
if((univ.current_pc().equip[i]) && (univ.current_pc().items[i].cursed)) {
shop_array.push_back(j);
break;

View File

@@ -2342,7 +2342,7 @@ short damage_pc(cPlayer& which_pc,short how_much,eDamageType damage_type,eRace t
// armor
if(damage_type == eDamageType::WEAPON || damage_type == eDamageType::UNDEAD || damage_type == eDamageType::DEMON) {
how_much -= minmax(-5,5,which_pc.status[eStatus::BLESS_CURSE]);
for(short i = 0; i < which_pc.items.size(); i++) {
for(short i = 0; i < cPlayer::INVENTORY_SIZE; i++) {
const cItem& item = which_pc.items[i];
if(item.variety != eItemType::NO_ITEM && which_pc.equip[i]) {
if((*item.variety).is_armour) {
@@ -2534,7 +2534,7 @@ void kill_pc(cPlayer& which_pc,eMainStatus type) {
if(combat_active_pc < 6 && &which_pc == &univ.party[combat_active_pc])
combat_active_pc = 6;
for(short i = 0; i < which_pc.items.size(); i++)
for(short i = 0; i < cPlayer::INVENTORY_SIZE; i++)
which_pc.equip[i] = false;
item_loc = is_combat() ? which_pc.combat_pos : univ.party.town_loc;

View File

@@ -67,7 +67,7 @@ bool handle_action(const sf::Event & event) {
break;
}
}
for(short i = 0; i < univ.party[current_active_pc].items.size(); i++) {
for(short i = 0; i < cPlayer::INVENTORY_SIZE; i++) {
if((the_point.in(item_string_rects[i][1])) && // drop item
univ.party[current_active_pc].items[i].variety != eItemType::NO_ITEM) {
flash_rect(item_string_rects[i][1]);

View File

@@ -344,7 +344,7 @@ void draw_items() {
return; // If PC is dead, it has no items
}
sf::Texture& invenbtn_gworld = *ResMgr::graphics.get("invenbtns");
for(short i = 0; i < univ.party[current_active_pc].items.size(); i++) // Loop through items and draw each
for(short i = 0; i < cPlayer::INVENTORY_SIZE; i++) // Loop through items and draw each
if(univ.party[current_active_pc].items[i].variety != eItemType::NO_ITEM) { // i.e. does item exist
std::string to_draw = std::to_string(i + 1) + ". ";
if(!univ.party[current_active_pc].items[i].ident)

View File

@@ -494,10 +494,25 @@ bool cPlayer::give_item(cItem item, int flags) {
}
return false;
}
cInvenSlot free_space = has_space();
if(!free_space || main_status != eMainStatus::ALIVE)
return false;
else {
if(main_status != eMainStatus::ALIVE) return false;
cInvenSlot real_free_space = has_space();
cInvenSlot extra_space = cInvenSlot(*this, INVENTORY_SIZE);
cInvenSlot* space_ptr = &real_free_space;
if(!real_free_space){
// Check if using the extra slot for stacking items would result in everything fitting:
*extra_space = item;
if(combine_things(true)){
space_ptr = &extra_space;
}
*extra_space = cItem();
}
cInvenSlot free_space = *space_ptr;
if(free_space) {
if(check_only) return true;
item.property = false;
@@ -518,8 +533,8 @@ bool cPlayer::give_item(cItem item, int flags) {
if(equip_type != 0 && (*item.variety).equip_count) {
if(!equip_item(free_space.slot, false) && equip_type != GIVE_EQUIP_SOFT) {
eItemCat exclude = (*item.variety).exclusion;
int rem1 = items.size(), rem2 = items.size();
for(int i = 0; i < items.size(); i++) {
int rem1 = INVENTORY_SIZE, rem2 = INVENTORY_SIZE;
for(int i = 0; i < INVENTORY_SIZE; i++) {
if(i == free_space.slot) continue;
if(!equip[i]) continue;
eItemCat check_exclude = (*items[i].variety).exclusion;
@@ -527,20 +542,20 @@ bool cPlayer::give_item(cItem item, int flags) {
if(exclude == eItemCat::MISC && item.variety != items[i].variety)
continue;
if(exclude == eItemCat::HANDS) {
if(rem1 == items.size()) {
if(item.variety == eItemType::ONE_HANDED || item.variety == eItemType::TWO_HANDED || rem2 < items.size())
if(rem1 == INVENTORY_SIZE) {
if(item.variety == eItemType::ONE_HANDED || item.variety == eItemType::TWO_HANDED || rem2 < INVENTORY_SIZE)
rem1 = i;
if(rem1 < items.size()) continue;
if(rem1 < INVENTORY_SIZE) continue;
}
if(rem2 == items.size()) {
if(item.variety == eItemType::SHIELD || item.variety == eItemType::SHIELD_2 || rem1 < items.size())
if(rem2 == INVENTORY_SIZE) {
if(item.variety == eItemType::SHIELD || item.variety == eItemType::SHIELD_2 || rem1 < INVENTORY_SIZE)
rem2 = i;
}
} else if(rem1 < items.size())
} else if(rem1 < INVENTORY_SIZE)
rem1 = i;
}
bool can_rem1 = rem1 < items.size() && (!items[rem1].cursed || equip_type == GIVE_EQUIP_FORCE);
bool can_rem2 = rem2 < items.size() && (!items[rem2].cursed || equip_type == GIVE_EQUIP_FORCE);
bool can_rem1 = rem1 < INVENTORY_SIZE && (!items[rem1].cursed || equip_type == GIVE_EQUIP_FORCE);
bool can_rem2 = rem2 < INVENTORY_SIZE && (!items[rem2].cursed || equip_type == GIVE_EQUIP_FORCE);
if(exclude == eItemCat::HANDS) {
if((*item.variety).num_hands == 2 && can_rem1 && can_rem2) {
equip[rem1] = false;
@@ -574,7 +589,7 @@ bool cPlayer::equip_item(int which_item, bool do_print) {
return false;
}
unsigned short num_this_type = 0, hands_occupied = 0;
for(int i = 0; i < items.size(); i++)
for(int i = 0; i < INVENTORY_SIZE; i++)
if(equip[i]) {
if(items[i].variety == item.variety)
num_this_type++;
@@ -584,7 +599,7 @@ bool cPlayer::equip_item(int which_item, bool do_print) {
eItemCat equip_item_type = (*item.variety).exclusion;
// Now if missile is already equipped, no more missiles
if(equip_item_type == eItemCat::MISSILE_AMMO || equip_item_type == eItemCat::MISSILE_WEAPON) {
for(int i = 0; i < items.size(); i++)
for(int i = 0; i < INVENTORY_SIZE; i++)
if(equip[i] && (*items[i].variety).exclusion == equip_item_type) {
if(do_print && print_result) {
print_result("Equip: You have something of this type");
@@ -659,7 +674,7 @@ short cPlayer::cur_weight() const {
short weight = 0;
bool airy = false,heavy = false;
for(int i = 0; i < items.size(); i++)
for(int i = 0; i < INVENTORY_SIZE; i++)
if(items[i].variety != eItemType::NO_ITEM) {
weight += items[i].item_weight();
if(items[i].ability == eItemAbil::LIGHTER_OBJECT)
@@ -682,7 +697,7 @@ short cPlayer::free_weight() const {
short cPlayer::armor_encumbrance() const {
short total = 0;
for(short i = 0; i < items.size(); i++) {
for(short i = 0; i < INVENTORY_SIZE; i++) {
if(equip[i]) total += items[i].awkward;
}
return total;
@@ -694,7 +709,7 @@ short cPlayer::total_encumbrance(const std::array<short, 51>& reduce_chance) con
short burden = free_weight();
if(burden < 0) total += burden / -10;
for(short i = 0; i < items.size(); i++)
for(short i = 0; i < INVENTORY_SIZE; i++)
if(equip[i]) {
short item_encumbrance = items[i].awkward;
if(items[i].ability == eItemAbil::ENCUMBERING)
@@ -709,7 +724,7 @@ short cPlayer::total_encumbrance(const std::array<short, 51>& reduce_chance) con
}
cInvenSlot cPlayer::has_space() {
for(int i = 0; i < items.size(); i++) {
for(int i = 0; i < INVENTORY_SIZE; i++) {
if(items[i].variety == eItemType::NO_ITEM)
return cInvenSlot(*this, i);
}
@@ -722,6 +737,7 @@ const cInvenSlot cPlayer::has_space() const {
bool cPlayer::combine_things(bool check_only) {
bool can_combine = false;
// Here it is correct to check items.size() because the extra slot is *for* combining things.
for(int i = 0; i < items.size(); i++) {
if(items[i].variety != eItemType::NO_ITEM && items[i].type_flag > 0 && items[i].ident) {
for(int j = i + 1; j < items.size(); j++)
@@ -750,7 +766,7 @@ bool cPlayer::combine_things(bool check_only) {
short cPlayer::get_prot_level(eItemAbil abil, short dat) const {
int sum = 0;
for(int i = 0; i < items.size(); i++) {
for(int i = 0; i < INVENTORY_SIZE; i++) {
if(items[i].variety == eItemType::NO_ITEM) continue;
if(items[i].ability != abil) continue;
if(!equip[i]) continue;
@@ -762,7 +778,7 @@ short cPlayer::get_prot_level(eItemAbil abil, short dat) const {
template<typename Fcn>
cInvenSlot cPlayer::find_item_matching(Fcn fcn) {
for(short i = 0; i < items.size(); i++)
for(short i = 0; i < INVENTORY_SIZE; i++)
if(items[i].variety != eItemType::NO_ITEM && fcn(i, items[i]))
return cInvenSlot(*this, i);
return cInvenSlot(*this);
@@ -878,7 +894,7 @@ eBuyStatus cPlayer::ok_to_buy(short cost,cItem item) const {
if(party->active_quests[item.item_level].status != eQuestStatus::AVAILABLE)
return eBuyStatus::HAVE_LOTS;
} else if(item.variety != eItemType::GOLD && item.variety != eItemType::FOOD) {
for(int i = 0; i < items.size(); i++)
for(int i = 0; i < INVENTORY_SIZE; i++)
if(items[i].variety != eItemType::NO_ITEM && items[i].type_flag == item.type_flag && items[i].charges > 123)
return eBuyStatus::HAVE_LOTS;
@@ -894,6 +910,11 @@ eBuyStatus cPlayer::ok_to_buy(short cost,cItem item) const {
}
void cPlayer::take_item(int which_item) {
if(which_item == INVENTORY_SIZE){
// Taking the extra slot which is only for pre-stacking items
items[INVENTORY_SIZE] = cItem();
return;
}
if(weap_poisoned.slot == which_item && status[eStatus::POISONED_WEAPON] > 0) {
if(print_result) print_result(" Poison lost.");
status[eStatus::POISONED_WEAPON] = 0;
@@ -902,12 +923,12 @@ void cPlayer::take_item(int which_item) {
if(weap_poisoned.slot > which_item && status[eStatus::POISONED_WEAPON] > 0)
weap_poisoned.slot--;
for(int i = which_item; i < 23; i++) {
for(int i = which_item; i < INVENTORY_SIZE-1; i++) {
items[i] = items[i + 1];
equip[i] = equip[i + 1];
}
items[23] = cItem();
equip[23] = false;
items[INVENTORY_SIZE-1] = cItem();
equip[INVENTORY_SIZE-1] = false;
}
void cPlayer::remove_charge(int which_item) {
@@ -1273,7 +1294,7 @@ void cPlayer::writeTo(cTagFile& file) const {
if(weap_poisoned) {
page["POISON"] << weap_poisoned.slot;
}
for(int i = 0; i < items.size(); i++) {
for(int i = 0; i < INVENTORY_SIZE; i++) {
if(items[i].variety != eItemType::NO_ITEM) {
auto& item_page = file.add();
item_page["ITEM"] << i;
@@ -1357,9 +1378,9 @@ void cPlayer::readFrom(const cTagFile& file) {
party->next_pc_id = max(unique_id + 1, party->next_pc_id);
}
} else if(page.getFirstKey() == "ITEM") {
size_t i = items.size();
size_t i = INVENTORY_SIZE;
page["ITEM"] >> i;
if(i >= items.size()) continue;
if(i >= INVENTORY_SIZE) continue;
items[i].readFrom(page);
}
}

View File

@@ -98,7 +98,10 @@ public:
short skill_pts;
short level;
short exp_adj;
std::array<cItem,INVENTORY_SIZE> items;
// Keep an extra slot for stackable items to go into before combine_things() is called.
// This slot is not actually storage space and will always be eItemType::NO_ITEM unless
// give_item() is still in progress.
std::array<cItem,INVENTORY_SIZE+1> items;
std::bitset<INVENTORY_SIZE> equip;
std::bitset<62> priest_spells;
std::bitset<62> mage_spells;

View File

@@ -1220,7 +1220,7 @@ void cUniverse::exportGraphics() {
used_graphics.insert(party[i].which_graphic - 10000 + j);
} else if(party[i].which_graphic >= 1000)
update_pcs[party[i].which_graphic - 1000].insert(&party[i]);
for(size_t j = 0; j < party[i].items.size(); j++) {
for(size_t j = 0; j < cPlayer::INVENTORY_SIZE; j++) {
check_item(party[i].items[j]);
}
}
@@ -1277,7 +1277,7 @@ void cUniverse::exportSummons() {
for(int i = 0; i < 6; i++) {
if(party[i].main_status == eMainStatus::ABSENT)
continue;
for(size_t j = 0; j < party[i].items.size(); j++) {
for(size_t j = 0; j < cPlayer::INVENTORY_SIZE; j++) {
if(party[i].items[j].variety == eItemType::NO_ITEM) continue;
if(party[i].items[j].ability == eItemAbil::SUMMONING || party[i].items[j].ability == eItemAbil::MASS_SUMMONING) {
mon_num_t monst = party[i].items[j].abil_data.value;