Add three new types of shop entries and implement support for limited stock in shops
- Random item of special class (works much the same as random item of treasure class) - Specific item, with percentage chance - Item whose purchase calls a special node - Limited stock is only supported for standard items, optional items, and special node hooks
This commit is contained in:
@@ -126,6 +126,23 @@ void start_shop_mode(short which,short cost_adj,std::string store_name) {
|
||||
stat_screen_mode = MODE_SHOP;
|
||||
shop_sbar->setPosition(0);
|
||||
|
||||
// Check if the shop's stock has been reduced yet
|
||||
if(univ.party.store_limited_stock.find(active_shop_num) != univ.party.store_limited_stock.end()) {
|
||||
for(auto p : univ.party.store_limited_stock[active_shop_num]) {
|
||||
int which_item, quant_left;
|
||||
std::tie(which_item, quant_left) = p;
|
||||
if(which_item < 0 || which_item >= 30) continue;
|
||||
cShopItem entry = active_shop.getItem(which_item);
|
||||
if(entry.quantity == 0) continue; // Just in case a stray entry accidentally gets put in for an infinite stock item
|
||||
if(quant_left == 0)
|
||||
entry.type = eShopItemType::EMPTY;
|
||||
else if(entry.type == eShopItemType::OPTIONAL)
|
||||
entry.quantity = quant_left + (entry.quantity / 1000) * 1000;
|
||||
else entry.quantity = quant_left;
|
||||
active_shop.replaceItem(which_item, entry);
|
||||
}
|
||||
}
|
||||
|
||||
set_up_shop_array();
|
||||
put_background();
|
||||
|
||||
@@ -181,6 +198,15 @@ void end_shop_mode() {
|
||||
else univ.party.magic_store_items[active_shop_num][i].variety = eItemType::NO_ITEM;
|
||||
}
|
||||
}
|
||||
// We also need to save any limited stock
|
||||
for(int i = 0; i < 30; i++) {
|
||||
cShopItem item = active_shop.getItem(i);
|
||||
if(item.quantity > 0) {
|
||||
// This means the stock is limited.
|
||||
int left = item.type == eShopItemType::EMPTY ? 0 : item.quantity;
|
||||
univ.party.store_limited_stock[active_shop_num][i] = left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_shop_event(location p) {
|
||||
@@ -224,6 +250,7 @@ void handle_shop_event(location p) {
|
||||
}
|
||||
|
||||
void handle_sale(cShopItem item, int i) {
|
||||
short s1, s2, s3; // Dummy variables to pass to run_special
|
||||
cItem base_item = item.item;
|
||||
short cost = item.getCost(active_shop.getCostAdjust());
|
||||
rectangle dummy_rect = {0,0,0,0};
|
||||
@@ -232,6 +259,8 @@ void handle_sale(cShopItem item, int i) {
|
||||
switch(item.type) {
|
||||
case eShopItemType::EMPTY: break; // Invalid
|
||||
case eShopItemType::TREASURE: break; // Also invalid
|
||||
case eShopItemType::CLASS: break; // Also invalid
|
||||
case eShopItemType::OPTIONAL: break; // Also invalid
|
||||
case eShopItemType::ITEM:
|
||||
switch(univ.party[current_pc].ok_to_buy(cost,base_item)) {
|
||||
case eBuyStatus::OK:
|
||||
@@ -240,8 +269,6 @@ void handle_sale(cShopItem item, int i) {
|
||||
univ.party[current_pc].give_item(base_item,true);
|
||||
// Magic shops have limited stock
|
||||
active_shop.takeOne(i);
|
||||
if(active_shop.size() != size_before)
|
||||
shop_sbar->setMaximum(shop_sbar->getMaximum() - 1);
|
||||
break;
|
||||
case eBuyStatus::NO_SPACE: ASB("Can't carry any more items."); break;
|
||||
case eBuyStatus::NEED_GOLD: ASB("Not enough cash."); break;
|
||||
@@ -341,6 +368,9 @@ void handle_sale(cShopItem item, int i) {
|
||||
give_help(41,0);
|
||||
}
|
||||
break;
|
||||
case eShopItemType::CALL_SPECIAL:
|
||||
run_special(eSpecCtx::SHOPPING, 0, base_item.item_level, {0,0}, &s1, &s2, &s3);
|
||||
break;
|
||||
case eShopItemType::SKILL:
|
||||
if(base_item.item_level < 0 || base_item.item_level > 18) {
|
||||
beep();
|
||||
@@ -381,6 +411,8 @@ void handle_info_request(cShopItem item) {
|
||||
switch(item.type) {
|
||||
case eShopItemType::EMPTY: break;
|
||||
case eShopItemType::TREASURE: break;
|
||||
case eShopItemType::CLASS: break;
|
||||
case eShopItemType::OPTIONAL: break;
|
||||
case eShopItemType::ITEM:
|
||||
display_pc_item(6,0, base_item,0);
|
||||
break;
|
||||
@@ -427,6 +459,9 @@ void handle_info_request(cShopItem item) {
|
||||
case eShopItemType::RESURRECT:
|
||||
cStrDlog("Select this option to resurrect a PC that has been turned to dust.", "", "Resurrect", 15, PIC_DLOG).show();
|
||||
break;
|
||||
case eShopItemType::CALL_SPECIAL:
|
||||
cStrDlog(base_item.desc, "", base_item.full_name, base_item.graphic_num, PIC_ITEM).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,7 +521,14 @@ void set_up_shop_array() {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eShopItemType::CALL_SPECIAL:
|
||||
if(PSD[entry.item.abil_data[0]][entry.item.abil_data[1]])
|
||||
shop_array[i++] = j;
|
||||
break;
|
||||
case eShopItemType::OPTIONAL:
|
||||
entry.quantity %= 1000;
|
||||
case eShopItemType::TREASURE:
|
||||
case eShopItemType::CLASS:
|
||||
entry.type = eShopItemType::ITEM;
|
||||
entry.item = univ.party.magic_store_items[active_shop_num][j];
|
||||
if(entry.item.variety == eItemType::NO_ITEM)
|
||||
|
@@ -954,7 +954,28 @@ void refresh_store_items() {
|
||||
allow_junk_treasure = true;
|
||||
univ.party.magic_store_items[i][j] = get_random_store_item(entry.item.item_level);
|
||||
allow_junk_treasure = false;
|
||||
} else univ.party.magic_store_items[i][j] = cItem();
|
||||
continue;
|
||||
} else if(entry.type == eShopItemType::CLASS) {
|
||||
std::set<int> choices;
|
||||
for(int i = 0; i < univ.scenario.scen_items.size(); i++) {
|
||||
if(univ.scenario.scen_items[i].special_class == entry.item.special_class)
|
||||
choices.insert(i);
|
||||
}
|
||||
int choice = get_ran(1,0,choices.size());
|
||||
if(choice < choices.size()) {
|
||||
auto iter = choices.begin();
|
||||
std::advance(iter, choice);
|
||||
univ.party.magic_store_items[i][j] = univ.scenario.scen_items[*iter];
|
||||
continue;
|
||||
}
|
||||
} else if(entry.type == eShopItemType::OPTIONAL) {
|
||||
int roll = get_ran(1,1,100);
|
||||
if(roll <= entry.quantity / 1000) {
|
||||
univ.party.magic_store_items[i][j] = entry.item;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
univ.party.magic_store_items[i][j] = cItem();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -554,6 +554,11 @@ void cParty::writeTo(std::ostream& file) const {
|
||||
file << "PLAYED " << scen_played << '\n';
|
||||
for(auto p : quest_status)
|
||||
file << "QUEST " << p.first << ' ' << p.second << ' ' << quest_start.at(p.first) << ' ' << quest_source.at(p.first) << '\n';
|
||||
for(auto p : store_limited_stock) {
|
||||
for(auto p2 : p.second) {
|
||||
file << "SHOPSTOCK " << p.first << p2.first << p2.second;
|
||||
}
|
||||
}
|
||||
for(auto iter = campaign_flags.begin(); iter != campaign_flags.end(); iter++){
|
||||
std::string campaign_id = maybe_quote_string(iter->first);
|
||||
// Okay, we have the campaign ID in a state such that reading it back in will restore the original ID.
|
||||
@@ -772,6 +777,9 @@ void cParty::readFrom(std::istream& file){
|
||||
int i;
|
||||
sin >> i;
|
||||
sin >> quest_status[i] >> quest_start[i] >> quest_source[i];
|
||||
} else if(cur == "SHOPSTOCK") {
|
||||
int i, j;
|
||||
sin >> i >> j >> store_limited_stock[i][j];
|
||||
}else if(cur == "KILLS")
|
||||
sin >> total_m_killed;
|
||||
else if(cur == "DAMAGE")
|
||||
|
@@ -94,6 +94,7 @@ public:
|
||||
short in_horse;
|
||||
cOutdoors::cCreature out_c[10];
|
||||
std::map<int,std::array<cItem,30>> magic_store_items;
|
||||
std::map<int,std::map<int,int>> store_limited_stock;
|
||||
std::vector<job_bank_t> job_banks;
|
||||
mon_num_t imprisoned_monst[4]; // Soul Crystal
|
||||
char m_noted[256]; // has the monster been scried?
|
||||
|
@@ -96,14 +96,16 @@ size_t cShop::size() {
|
||||
});
|
||||
}
|
||||
|
||||
void cShop::addItem(cItem item, size_t quantity) {
|
||||
void cShop::addItem(cItem item, size_t quantity, int chance) {
|
||||
size_t i = firstEmpty();
|
||||
if(i >= items.size()) return;
|
||||
if(item.variety == eItemType::NO_ITEM) return;
|
||||
items[i].type = eShopItemType::ITEM;
|
||||
items[i].type = chance == 100 ? eShopItemType::ITEM : eShopItemType::OPTIONAL;
|
||||
items[i].item = item;
|
||||
items[i].item.ident = true;
|
||||
items[i].quantity = quantity;
|
||||
if(chance < 100)
|
||||
items[i].quantity = min(999,quantity) + chance * 1000;
|
||||
}
|
||||
|
||||
static cItem store_mage_spells(short which_s) {
|
||||
@@ -174,6 +176,8 @@ void cShop::addSpecial(eShopItemType type, int n) {
|
||||
};
|
||||
if(type == eShopItemType::EMPTY) return;
|
||||
if(type == eShopItemType::ITEM) return;
|
||||
if(type == eShopItemType::OPTIONAL) return;
|
||||
if(type == eShopItemType::CALL_SPECIAL) return;
|
||||
size_t i = firstEmpty();
|
||||
if(i >= items.size()) return;
|
||||
items[i].type = type;
|
||||
@@ -188,6 +192,10 @@ void cShop::addSpecial(eShopItemType type, int n) {
|
||||
items[i].item.full_name = get_str("skills", n * 2 + 1);
|
||||
} else if(type == eShopItemType::TREASURE) {
|
||||
items[i].item.item_level = n;
|
||||
items[i].item.full_name = "Treasure (type " + std::to_string(n) + ")";
|
||||
} else if(type == eShopItemType::CLASS) {
|
||||
items[i].item.special_class = n;
|
||||
items[i].item.full_name = "Item of special class " + std::to_string(n);
|
||||
} else {
|
||||
items[i].item.graphic_num = 109;
|
||||
items[i].item.full_name = heal_types[int(type) - int(eShopItemType::HEAL_WOUNDS)];
|
||||
@@ -199,6 +207,18 @@ void cShop::addSpecial(eShopItemType type, int n) {
|
||||
items[i].quantity = 0;
|
||||
}
|
||||
|
||||
void cShop::addSpecial(std::string name, std::string descr, pic_num_t pic, int node, int cost, int quantity) {
|
||||
size_t i = firstEmpty();
|
||||
if(i >= items.size()) return;
|
||||
items[i].type = eShopItemType::CALL_SPECIAL;
|
||||
items[i].quantity = quantity;
|
||||
items[i].item.full_name = name;
|
||||
items[i].item.desc = descr;
|
||||
items[i].item.graphic_num = pic;
|
||||
items[i].item.item_level = node;
|
||||
items[i].item.value = cost;
|
||||
}
|
||||
|
||||
cShopItem cShop::getItem(size_t i) const {
|
||||
return items[i];
|
||||
}
|
||||
@@ -232,9 +252,9 @@ void cShop::setCostAdjust(int adj) {
|
||||
}
|
||||
|
||||
void cShop::takeOne(size_t i) {
|
||||
if(items[i].quantity == 1)
|
||||
clearItem(i);
|
||||
else if(items[i].quantity > 0)
|
||||
if(items[i].quantity == 1) {
|
||||
items[i].type = eShopItemType::EMPTY;
|
||||
} else if(items[i].quantity > 0)
|
||||
items[i].quantity--;
|
||||
}
|
||||
|
||||
|
@@ -27,6 +27,9 @@ enum class eShopItemType {
|
||||
ALCHEMY = 4,
|
||||
SKILL,
|
||||
TREASURE,
|
||||
CLASS,
|
||||
OPTIONAL,
|
||||
CALL_SPECIAL,
|
||||
HEAL_WOUNDS,
|
||||
CURE_POISON,
|
||||
CURE_DISEASE,
|
||||
@@ -59,7 +62,8 @@ public:
|
||||
cShop();
|
||||
cShop(eShopType type, eShopPrompt prompt, pic_num_t pic, int adj, std::string name);
|
||||
explicit cShop(long preset);
|
||||
void addItem(cItem item, size_t quantity);
|
||||
void addItem(cItem item, size_t quantity, int chance = 100);
|
||||
void addSpecial(std::string name, std::string descr, pic_num_t pic, int node, int cost, int quantity);
|
||||
void addSpecial(eShopItemType type, int n = 0);
|
||||
template<typename Iter> void addItems(Iter begin, Iter end, size_t quantity) {
|
||||
while(begin != end) addItem(*begin++, quantity);
|
||||
|
@@ -526,6 +526,7 @@ enum class eSpecCtx {
|
||||
ATTACKED_MELEE = 23,
|
||||
ATTACKED_RANGE = 24,
|
||||
HAIL = 25, // Special called by trying to initiate conversation
|
||||
SHOPPING = 26,
|
||||
};
|
||||
|
||||
enum class eSpecType {
|
||||
|
Reference in New Issue
Block a user