Fix scenario editor build and update data/docs regarding shop nodes

This commit is contained in:
2015-02-03 13:53:03 -05:00
parent 5140f54a23
commit fcd29fb2ac
13 changed files with 45 additions and 115 deletions

View File

@@ -124,7 +124,12 @@ the party is in town A, the response is Text 1. Otherwise, its Text 2.</p>
<p>When one of these nodes is a response, conversation temporarily ends, and a different sort of screen appears.</p>
<p><b>Node Type 7 - Shop</b> A shopping screen appears. WHen the party is done shopping (and the player hits the done button), talking resumes. Shop where the party can buy items. A is the cost adjustment (Range 0 ... 6, see below). B is the number of the first thing in the shop. C is the total number of things in the shop, taken in order starting with B (press the Choose button to select). D is the type of shop. Text 1 is the name of the shop.</p>
<p><b>Node Type 7 - Shop</b> A shopping screen appears. When the party is done shopping
(and the player hits the done button), talking resumes. A is the cost adjustment (Range 0
... 6, see below). B is the number of the shop (shop 0 is usually the standard healing
shop; but if the scenario was created with an older version of the BoE Scenario Editor,
0-4 are the magic random item shops and 5 is the standard healing shop). C and D are
ignored Text 1 is the name of the shop.</p>
<p>The meaning of the cost adjustments are:</p>
<ol start='0'>
<li>Extremely Cheap</li>
@@ -135,29 +140,10 @@ the party is in town A, the response is Text 1. Otherwise, its Text 2.</p>
<li>Exorbitant</li>
<li>Utterly Ridiculous</li>
</ol>
<p>The possible shop types are as follows:</p>
<ol start='0'>
<li>Ordinary items shop; B is the number of the first item from the scenario's item
list.</li>
<li>Mage spells shop; B is the number of the first mage spell (0 - 61, but generally not
lower than 30).</li>
<li>Priest spells shop; B is the number of the first priest spell (0 - 61, but generally
not lower than 30).</li>
<li>Alchemy shop; B is the number of the first recipe (0 - 19).</li>
<li>Healing shop; B and C are ignored.</li>
<li>Random Items Shop, type 0. This brings up a shop window where the party can buy up to
10 randomly chosen items. These items are changed every 3000 moves, and are often magical.
There are five separate random lists maintained by the game. B and C are ignored for these
shops.</li>
<li>Random Items Shop, type 1.</li>
<li>Random Items Shop, type 2.</li>
<li>Random Items Shop, type 3.</li>
<li>Random Items Shop, type 4.</li>
<li>Skills Shop; B is the number of the first skill (0 - 18).</li>
</ol>
<p>Example: If A is 1, B is 193, C is 15, and Text 1 is Fred's Fish, and this talking node
is the response, the player gets to shop in a store called Fred's Fish. The prices are
quite cheap, and the player can buy items 193-207.</p>
<!-- TODO: Above example no longer works -->
<p><b>Node Type 8 - Training</b> The training window immediately comes up. Text 1 &amp; 2
are ignored.</p>
<p><b>Node Type 9 - Job Board</b> Brings up the job board, where the player can choose to

View File

@@ -74,18 +74,10 @@ type. When called, the special encounter ends immediately (the Jumpto field is i
and the party is not allowed to enter the space they were moving into. The game immediately
goes into shopping mode. Although normally used outdoors, this special node also works in
town.</p>
<p>In fields Extra 1a and Extra 1b, enter the number of the first item in the scenarios
item list that the store sells, and the total number of items the store sells. If the
numbers given are, for example, 50 and 10, the store sells items 50-59 from your scenarios
item list. If the store type is a junk shop, these fields are ignored.</p>
<dl>
<dt>Mess1:</dt><dd>This message is the name of the store the party has found.</dd>
<dt>Extra 1a:</dt><dd>The number of the item. This is the first item in the stores item
list. Press the Choose button to pick an item.</dd>
<dt>Extra 1b:</dt><dd>The type of store. (0 - items, 1 - mage spells, 2 - priest spells, 3
- alchemy, 4 - healing, 5-9 - junk, 10 - skills)</dd>
<dt>Extra 2a:</dt><dd>The number of items in the store. (Range 1 ... 40)</dd>
<dt>Extra 2b:</dt><dd>The item cost adjuster, a number from 0 (very cheap) to 6 (very
<dt>Extra 1a:</dt><dd>The number of the shop.</dd>
<dt>Extra 1b:</dt><dd>The item cost adjuster, a number from 0 (very cheap) to 6 (very
expensive). See chapter 11 (Dialogue) for the complete list of cost adjustments.</dd></dd>
</dl>

View File

@@ -70,13 +70,13 @@ Unused
Unused
Unused
Unused
Number of first item in store
Store type (see docs. for list)
Unused
Number of items in store (1 .. 40)
Which Shop
Cost adjust (0 .. 6, lower = cheaper)
Unused
Unused
Unused
Unused
Unused
--------------------
Display Small Message
Unused

View File

@@ -49,9 +49,9 @@ Response if in this town ...
Otherwise respond ...
Shop
Cost adjustment (0 .. 6)
Number of first item in shop
Total number of items in shop
Type of shop
Which shop
Unused
Unused
Name of shop
Unused
Receive Training

View File

@@ -62,12 +62,14 @@ cShop::cShop(long preset) {
type = eShopType::RANDOM;
prompt = eShopPrompt::SHOPPING;
face = 0;
name = "Magic Shop";
for(int i = 0; i < 10; i++)
addSpecial(eShopItemType::TREASURE, loot_index[i]);
} else if(preset == 'heal') {
type = eShopType::ALLOW_DEAD;
prompt = eShopPrompt::HEALING;
face = 41;
name = "Healing";
addSpecial(eShopItemType::HEAL_WOUNDS);
addSpecial(eShopItemType::CURE_POISON);
addSpecial(eShopItemType::CURE_DISEASE);

View File

@@ -363,7 +363,6 @@ std::istream& operator >> (std::istream& in, eSpecType& e) {
// T - Choose button to select a town
// i - Choose button to select an item
// I - Choose button to select a special item
// # - Choose button to select store item (uses ex1b for type)
// t - Choose button to select a terrain type
// c - Choose button to select a monster type
// a - Choose button to select an alchemy recipe
@@ -384,7 +383,7 @@ std::istream& operator >> (std::istream& in, eSpecType& e) {
// / - Choose button to select generic stairway text
// : - Choose stairway trigger conditions
// L - Choose button to select a town lighting type
// & - Choose button to select shop type
// & - Choose button to select a shop
// % - Choose button to select shop cost adjustment
// { - Choose button to select a spell pattern
// } - As above, but allows you to select which version of the rotateable field
@@ -402,11 +401,11 @@ static const char*const button_dict[7][11] = {
" ", // msg3
" p 3 ", // pic
" ? ", // pictype
" # x T i M cit j", // ex1a
" & S ss cJ", // ex1b
" & x T i M cit j", // ex1a
" % S ss cJ", // ex1b
" ", // ex1c
" tt ", // ex2a
" % t ", // ex2b
" t ", // ex2b
" ", // ex2c
}, { // one-shot nodes
"mm md d mmm", // msg1

View File

@@ -17,8 +17,6 @@
void cSpeech::append(legacy::talking_record_type& old, std::vector<shop_info_t>& shops){
int i,j;
for(i = 0; i < 200; i++)
strlens[i] = old.strlens[i];
for(i = 0; i < 60; i++){
talk_nodes[i].personality = old.talk_nodes[i].personality;
for(j = 0; j < 4; j++){
@@ -51,7 +49,7 @@ void cSpeech::append(legacy::talking_record_type& old, std::vector<shop_info_t>&
break;
case 7: // Item shop
talk_nodes[i].type = eTalkNode::SHOP;
shops.push_back({eShopItemType::ITEM, talk_nodes[i].extras[1], talk_nodes[i].extras[2]});
shops.push_back({eShopItemType::ITEM, talk_nodes[i].extras[1], talk_nodes[i].extras[2], talk_nodes[i].str1});
talk_nodes[i].extras[1] = shops.size() + 5;
talk_nodes[i].extras[2] = 0;
break;
@@ -64,14 +62,15 @@ void cSpeech::append(legacy::talking_record_type& old, std::vector<shop_info_t>&
shops.push_back({
old.talk_nodes[i].type == 9 ? eShopItemType::MAGE_SPELL : eShopItemType::PRIEST_SPELL,
talk_nodes[i].extras[1] + 30,
talk_nodes[i].extras[2]
talk_nodes[i].extras[2],
talk_nodes[i].str1
});
talk_nodes[i].extras[1] = shops.size() + 5;
talk_nodes[i].extras[2] = 0;
break;
case 11: // Alchemy shop
talk_nodes[i].type = eTalkNode::SHOP;
shops.push_back({eShopItemType::ALCHEMY, talk_nodes[i].extras[1], talk_nodes[i].extras[2]});
shops.push_back({eShopItemType::ALCHEMY, talk_nodes[i].extras[1], talk_nodes[i].extras[2], talk_nodes[i].str1});
talk_nodes[i].extras[1] = shops.size() + 5;
talk_nodes[i].extras[2] = 0;
break;

View File

@@ -29,6 +29,7 @@ public:
struct shop_info_t {
eShopItemType type;
int first, count;
std::string name;
};
class cSpeech { // formerly talking_record_type
@@ -42,7 +43,6 @@ public:
std::string str1, str2;
cNode() {std::fill(extras, extras + 4, -1);}
};
unsigned char strlens[200];
cPersonality people[10];
cNode talk_nodes[60];

View File

@@ -135,8 +135,6 @@ cTown::cTown(cScenario& scenario, bool init_strings) : scenario(scenario) {
sign_strs[i-120] = temp_str;
}
for(i = 0; i < 200; i++)
talking.strlens[i] = 0;
for(i = 0; i < 10; i++) {
talking.people[i].title = "Unused";
talking.people[i].look = "";

View File

@@ -2518,6 +2518,7 @@ bool build_scenario() {
// TODO: This will probably change drastically once the new scenario format is implemented
make_new_scenario(filename,width,height,default_town,grass);
scenario.shops.push_back(cShop('heal'));
overall_mode = MODE_MAIN_SCREEN;

View File

@@ -292,11 +292,12 @@ short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent, con
}
break;
case STRT_CONTEXT:
// TODO: This is a discontinous list, so there's probably a better way to deal with it...
strings = *ResMgr::get<StringRsrc>("special-contexts");
break;
case STRT_SHOP:
strings = {"Items", "Mage Spells", "Priest Spells", "Alchemy", "Healing", "Magic Shop: Junk", "Magic Shop: Lousy", "Magic Shop: So-so", "Magic Shop: Good", "Magic Shop: Great", "Skill Shop"};
for(cShop& shop : scenario.shops) {
strings.push_back(shop.getName());
}
break;
case STRT_COST_ADJ:
strings = {"Extremely Cheap", "Very Reasonable", "Pretty Average", "Somewhat Pricey", "Expensive", "Exorbitant", "Utterly Ridiculous"};
@@ -711,18 +712,6 @@ static bool edit_spec_enc_value(cDialog& me, std::string item_hit, node_stack_t&
if(type == eSpecType::CALL_GLOBAL) btn = 'S';
else btn = 's';
}
if(btn == '#') {
switch(eShopType(me["x1b"].getTextAsNum())) {
case eShopType::ITEMS: btn = 'i'; break;
case eShopType::MAGE: btn = 'A'; break;
case eShopType::PRIEST: btn = 'P'; break;
case eShopType::ALCHEMY: btn = 'a'; break;
case eShopType::SKILLS: btn = 'k'; break;
case eShopType::HEALING: case eShopType::MAGIC_GOOD: case eShopType::MAGIC_GREAT:
case eShopType::MAGIC_JUNK: case eShopType::MAGIC_LOUSY: case eShopType::MAGIC_SO_SO:
break;
}
}
short val = me[field].getTextAsNum(), mode = (btn == 'S' || btn == '$') ? 0 : edit_stack.top().mode, store;
short pictype = me["pictype"].getTextAsNum();
bool choose_string = true;
@@ -797,7 +786,7 @@ static bool edit_spec_enc_value(cDialog& me, std::string item_hit, node_stack_t&
case '@': strt = STRT_ATTITUDE; title = "What attitude?"; break;
case '/': strt = STRT_STAIR; title = "Which stairway text?"; break;
case 'L': strt = STRT_LIGHT; title = "What lighting type?"; break;
case '&': strt = STRT_SHOP; title = "What shop type?"; break;
case '&': strt = STRT_SHOP; title = "Which shop?"; break;
case '%': strt = STRT_COST_ADJ; title = "What cost adjust?"; break;
case '*': strt = STRT_CONTEXT; title = "What context?"; break;
case ':': strt = STRT_STAIR_MODE; title = "Select trigger limitations:"; break;
@@ -810,11 +799,6 @@ static bool edit_spec_enc_value(cDialog& me, std::string item_hit, node_stack_t&
store = choose_graphic(val, pics[btn - '0'], &me);
if(store < 0) store = val;
break;
case '#':
choose_string = false;
store = val;
giveError("Either you have not chosen a shop type yet, or the shop type you chose doesn't allow you to customize its items.",&me);
break;
default:
choose_string = false;
store = val;

View File

@@ -1014,7 +1014,6 @@ static bool save_talk_node(cDialog& me, std::string item_hit, std::stack<short>&
for(int i = 0; i < 4; i++)
talk_node.extras[i] = me["extra" + std::to_string(i + 1)].getTextAsNum();
int n;
switch(talk_node.type) {
case eTalkNode::DEP_ON_SDF: case eTalkNode::SET_SDF:
if(cre(talk_node.extras[0],0,299,"First part of Stuff Done flag must be from 0 to 299.","",&me)) return false;
@@ -1035,20 +1034,7 @@ static bool save_talk_node(cDialog& me, std::string item_hit, std::stack<short>&
break;
case eTalkNode::SHOP:
if(cre(talk_node.extras[0],0,6,"Cost adjustment must be from 0 (cheapest) to 6 (most expensive).","",&me)) return false;
if(cre(talk_node.extras[3],0,11,"Shop type must be from 0 to 11.", "", &me)) return false;
switch(talk_node.extras[3]) {
case int(eShopType::ITEMS): n = scenario.scen_items.size(); break;
case int(eShopType::MAGE): case int(eShopType::PRIEST): n = 62; break;
case int(eShopType::ALCHEMY): n = 20; break;
case int(eShopType::SKILLS): n = 18; break;
default: n = 0; break;
}
if(n > 0) {
if(cre(talk_node.extras[1],0,n,"First item in shop must be from 0 to " + std::to_string(n), "", &me)) return false;
if(cre(talk_node.extras[1],1,30,"Shops cannot have more than 30 items.", "", &me)) return false;
int last = talk_node.extras[1] + talk_node.extras[2] - 1;
if(cre(last,0,n,"Number of items in shop cannot be such that the last item does not exist.", "", &me)) return false;
}
if(cre(talk_node.extras[3],0,scenario.shops.size() - 1,"Which shop must refer to an existing shop.", "", &me)) return false;
break;
case eTalkNode::RECEIVE_QUEST:
if(cre(talk_node.extras[0], 0, scenario.quests.size() - 1, "Quest must be an existing quest.", "", &me)) return false;
@@ -1160,27 +1146,8 @@ static bool select_talk_node_value(cDialog& me, std::string item_hit, const std:
const auto& talk_node = town->talking.talk_nodes[talk_edit_stack.top()];
if(item_hit == "chooseB") {
int i = me["extra2"].getTextAsNum();
switch(talk_node.extras[3]) {
case int(eShopType::MAGE):
i = choose_text(STRT_MAGE,i,&me,"What is the first mage spell in the shop?");
break;
case int(eShopType::PRIEST):
i = choose_text(STRT_PRIEST,i,&me,"What is the first priest spell in the shop?");
break;
case int(eShopType::ALCHEMY):
i = choose_text(STRT_ALCHEMY,i,&me,"What is the first recipe in the shop?");
break;
case int(eShopType::ITEMS):
i = choose_text(STRT_ITEM,i,&me,"What is the first item in the shop?");
break;
case int(eShopType::SKILLS):
i = choose_text(STRT_SKILL,i,&me,"What is the first skill in the shop?");
break;
default:
giveError("The shop type you've chosen doesn't support item customization!", &me);
break;
}
me["extra1"].setTextToNum(i);
i = choose_text(STRT_SHOP,i,&me,"Which shop?");
me["extra2"].setTextToNum(i);
} else if(item_hit == "chooseA") {
int spec = me["extra1"].getTextAsNum();
int mode = talk_node.type == eTalkNode::CALL_TOWN_SPEC ? 2 : 0;

View File

@@ -45,10 +45,10 @@ 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) {
template<typename Container> static void port_shop_spec_node(cSpecial& spec, std::vector<shop_info_t>& shops, Container strs) {
int which_shop;
if(spec.ex1b < 4) {
shops.push_back({eShopItemType(spec.ex1b + 1), spec.ex1a, spec.ex2a});
shops.push_back({eShopItemType(spec.ex1b + 1), spec.ex1a, spec.ex2a, strs[spec.m1]});
which_shop = shops.size() + 5;
} else if(spec.ex1b == 4)
which_shop = 5;
@@ -174,19 +174,19 @@ bool load_scenario_v1(fs::path file_to_load, cScenario& scenario){
// 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);
port_shop_spec_node(spec, shops, scenario.spec_strs);
}
for(cOutdoors* out : scenario.outdoors) {
for(cSpecial& spec : out->specials) {
if(spec.type == eSpecType::ENTER_SHOP)
port_shop_spec_node(spec, shops);
port_shop_spec_node(spec, shops, out->spec_strs);
}
}
// 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);
port_shop_spec_node(spec, shops, town->spec_strs);
}
}
@@ -214,7 +214,7 @@ bool load_scenario_v1(fs::path file_to_load, cScenario& scenario){
prompt = eShopPrompt::ALCHEMY;
type = eShopType::ALLOW_DEAD;
}
cShop shop(type, prompt, face, 0, "");
cShop shop(type, prompt, face, 0, info.name);
if(info.type == eShopItemType::ITEM) {
int end = info.first + info.count;
end = min(end, scenario.scen_items.size());
@@ -338,10 +338,9 @@ 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, shops);
for(i = 0; i < 170; i++) {
len = (long) (the_town.talking.strlens[i]);
len = (long) (store_talk.strlens[i]);
n = fread(temp_str, len, 1, file_id);
temp_str[len] = 0;
if(i >= 0 && i < 10)
@@ -361,6 +360,9 @@ bool load_town_v1(fs::path scen_file, short which_town, cTown& the_town, legacy:
}
}
// Do this after strings are loaded because porting shops requires access to strings
the_town.talking.append(store_talk, shops);
n = fclose(file_id);
if(n != 0) {
oopsError(18, 0, 0);