Rewrite shops system to be more general

- Shops can now contain more than one type of item (for example, both mage and priest spells)
- Shops can now specify any talking portrait
- Healing options in a shop now have an info button giving a brief explanation (partly because it was easier than maintaining the exception)
- Additional healing option (not present in default healing shops): cure acid
- Acid is also no longer removed when you leave town or end combat (unless it's about to wear off)
- Always start shop mode with the scrollbar scrolled to the top
- Fix crash when entering an outdoor shop (and outdoor dialogue for that matter)
- Treasure generation system can now return junk items (treasure type 0), but only if explicitly requested; this is possible in shops but not with monsters since 0 means no loot

Dialog Engine:
- When setting a scrollbar's maximum, it now ensures the current position doesn't end up greater than the maximum
This commit is contained in:
2015-02-03 10:22:28 -05:00
parent 559663dd35
commit 008248a947
20 changed files with 435 additions and 238 deletions

View File

@@ -17,6 +17,7 @@
#include "regtown.h"
#include "map_parse.hpp"
#include "graphtool.hpp"
#include "mathutil.hpp"
#include "porting.hpp"
#include "restypes.hpp"
@@ -32,7 +33,7 @@ void load_spec_graphics(fs::path scen_file);
// Load old scenarios (town talk is handled by the town loading function)
static bool load_scenario_v1(fs::path file_to_load, cScenario& scenario);
static bool load_outdoors_v1(fs::path scen_file, location which_out,cOutdoors& the_out, legacy::scenario_data_type& scenario);
static bool load_town_v1(fs::path scen_file, short which_town, cTown& the_town, legacy::scenario_data_type& scenario);
static bool load_town_v1(fs::path scen_file,short which_town,cTown& the_town,legacy::scenario_data_type& scenario,std::vector<shop_info_t>& shops);
// Load new scenarios
static bool load_outdoors(fs::path out_base, location which_out, cOutdoors& the_out);
static bool load_town(fs::path town_base, short which_town, cTown*& the_town);
@@ -44,6 +45,20 @@ bool load_scenario(fs::path file_to_load, cScenario& scenario) {
return load_scenario_v1(file_to_load, scenario);
}
static void port_shop_spec_node(cSpecial& spec, std::vector<shop_info_t>& shops) {
int which_shop;
if(spec.ex1b < 4) {
shops.push_back({eShopItemType(spec.ex1b + 1), spec.ex1a, spec.ex2a});
which_shop = shops.size() + 5;
} else if(spec.ex1b == 4)
which_shop = 5;
else which_shop = spec.ex1b - 5;
spec.ex1a = which_shop;
spec.ex1b = spec.ex2b;
spec.ex2a = spec.ex2b = -1;
}
bool load_scenario_v1(fs::path file_to_load, cScenario& scenario){
short i,n;
bool file_ok = false;
@@ -140,6 +155,9 @@ bool load_scenario_v1(fs::path file_to_load, cScenario& scenario){
}
}
// Need to build a list of shops used in the scenario
std::vector<shop_info_t> shops;
// Then load all the towns
scenario.towns.resize(scenario.format.num_towns);
for(int i = 0; i < scenario.format.num_towns; i++) {
@@ -148,11 +166,70 @@ bool load_scenario_v1(fs::path file_to_load, cScenario& scenario){
case 1: scenario.towns[i] = new cMedTown(scenario); break;
case 2: scenario.towns[i] = new cTinyTown(scenario); break;
}
load_town_v1(scenario.scen_file, i, *scenario.towns[i], *temp_scenario);
load_town_v1(scenario.scen_file, i, *scenario.towns[i], *temp_scenario, shops);
}
// Enable character creation in starting town
scenario.towns[scenario.which_town_start]->has_tavern = true;
// Check special nodes in case there were outdoor shops
for(cSpecial& spec : scenario.scen_specials) {
if(spec.type == eSpecType::ENTER_SHOP)
port_shop_spec_node(spec, shops);
}
for(cOutdoors* out : scenario.outdoors) {
for(cSpecial& spec : out->specials) {
if(spec.type == eSpecType::ENTER_SHOP)
port_shop_spec_node(spec, shops);
}
}
// We'll check town nodes too, just in case.
for(cTown* town : scenario.towns) {
for(cSpecial& spec : town->specials) {
if(spec.type == eSpecType::ENTER_SHOP)
port_shop_spec_node(spec, shops);
}
}
// Now we have to build all those shops
for(shop_info_t info : shops) {
pic_num_t face = 0;
if(info.type == eShopItemType::MAGE_SPELL || info.type == eShopItemType::PRIEST_SPELL || info.type == eShopItemType::ALCHEMY)
face = 43;
else if(info.type == eShopItemType::ITEM) {
bool is_food_shop = true;
for(int i = info.first; i < info.first + info.count && i < scenario.scen_items.size(); i++) {
if(scenario.scen_items[i].variety != eItemType::FOOD)
is_food_shop = false;
}
if(is_food_shop)
face = 42;
}
eShopType type = eShopType::NORMAL;
eShopPrompt prompt = eShopPrompt::SHOPPING;
if(info.type == eShopItemType::MAGE_SPELL)
prompt = eShopPrompt::MAGE;
else if(info.type == eShopItemType::PRIEST_SPELL)
prompt = eShopPrompt::PRIEST;
else if(info.type == eShopItemType::ALCHEMY) {
prompt = eShopPrompt::ALCHEMY;
type = eShopType::ALLOW_DEAD;
}
cShop shop(type, prompt, face, 0, "");
if(info.type == eShopItemType::ITEM) {
int end = info.first + info.count;
end = min(end, scenario.scen_items.size());
shop.addItems(scenario.scen_items.begin() + info.first, scenario.scen_items.begin() + end, cShop::INFINITE);
} else {
int max = 62;
if(info.type == eShopItemType::ALCHEMY)
max = 20;
int end = min(max, info.first + info.count);
for(int i = info.first; i < end; i++)
shop.addSpecial(info.type, i);
}
scenario.shops.push_back(shop);
}
delete temp_scenario;
delete item_data;
return true;
@@ -179,7 +256,7 @@ static long get_town_offset(short which_town, legacy::scenario_data_type& scenar
return len_to_jump;
}
bool load_town_v1(fs::path scen_file, short which_town, cTown& the_town, legacy::scenario_data_type& scenario) {
bool load_town_v1(fs::path scen_file, short which_town, cTown& the_town, legacy::scenario_data_type& scenario, std::vector<shop_info_t>& shops) {
short i,n;
long len,len_to_jump = 0;
char temp_str[256];
@@ -261,7 +338,7 @@ bool load_town_v1(fs::path scen_file, short which_town, cTown& the_town, legacy:
return false;
}
port_talk_nodes(&store_talk);
the_town.talking.append(store_talk);
the_town.talking.append(store_talk, shops);
for(i = 0; i < 170; i++) {
len = (long) (the_town.talking.strlens[i]);