Merge all the shopping talk nodes together into a single node type

Also:
- Fix items in magic shops respawning when re-entering the shop
- Remove unused food shop type
This commit is contained in:
2015-01-30 12:56:01 -05:00
parent d568bc6858
commit 2de0b76454
9 changed files with 134 additions and 120 deletions

View File

@@ -122,25 +122,33 @@ the party is in town A, the response is Text 1. Otherwise, its Text 2.</p>
<h3>Shopping Talking Nodes</h3>
<p>When one of these nodes is a response, conversation temporarily ends, and a shopping
screen appears. When the party is done shopping (and the player hits the done button)
talking resumes. Most of these require you to specify a cost adjust, which is one of the
following:</p>
<ul>
<li>0 - Extremely Cheap</li>
<li>1 - Very Reasonable</li>
<li>2 - Pretty Average</li>
<li>3 - Somewhat Pricey</li>
<li>4 - Expensive</li>
<li>5 - Exorbitant</li>
<li>6 - Utterly Ridiculous</li>
</ul>
<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 - Buy Items</b> Shop where the party can buy items. A is the cost
adjustment (Range 0 ... 6, see above). B is the number of the first item in the shop (from
the scenarios item list). C is the total number of items in the shop, taken in order from
the scenario item list starting with B (press the Choose button to select). 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. 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>The meaning of the cost adjustments are:</p>
<ol start='0'>
<li>Extremely Cheap</li>
<li>Very Reasonable</li>
<li>Pretty Average</li>
<li>Somewhat Pricey</li>
<li>Expensive</li>
<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. 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>

View File

@@ -47,11 +47,11 @@ Unused
Unused
Response if in this town ...
Otherwise respond ...
Buy Items
Shop
Cost adjustment (0 .. 6)
Number of first item in shop
Total number of items in shop
Unused
Type of shop
Name of shop
Unused
Receive Training
@@ -61,33 +61,33 @@ Unused
Unused
Unused
Unused
Mage Spell Shop
Cost adjustment (0 .. 6)
Number of first spell in shop
Total number of spells in shop
Unused
Name of shop
Unused
Priest Spell Shop
Cost adjustment (0 .. 6)
Number of first spell in shop
Total number of spells in shop
Unused
Name of shop
Unused
Alchemy Shop
Cost adjustment (0 .. 6)
Number of first recipe in shop
Total number of recipes in shop
Unused
Name of shop
Unused
Healer
Cost adjustment (0 .. 6)
Unused Node Type
Unused
Unused
Unused
Unused
Unused
Unused
Unused Node Type
Unused
Unused
Unused
Unused
Unused
Unused
Unused Node Type
Unused
Unused
Unused
Unused
Unused
Unused
Unused Node Type
Unused
Unused
Unused
Unused
Unused
Name of healer
Unused
Sell Weapons
Unused
@@ -159,12 +159,12 @@ Unused
Unused
Response if party buys it
Response if party can't afford it
Magic Shop
Cost adjustment (0 .. 6)
Which shop (0 .. 4)
Unused Node Type
Unused
Unused
Unused
Unused
Unused
Name of shop
Unused
Reveal Town Location
Cost to know where town is

View File

@@ -173,6 +173,19 @@ void end_shop_mode() {
put_pc_screen();
// TODO: I suspect REFRESH_NONE will suffice here
redraw_screen(REFRESH_TERRAIN | REFRESH_BAR);
// If it was a magic shop, we need to update the stored list of items so that bought items don't reappear
if(active_shop.getType() >= eShopType::MAGIC_JUNK && active_shop.getType() <= eShopType::MAGIC_GREAT) {
int which_shop = int(active_shop.getType()) - int(eShopType::MAGIC_JUNK);
int j = 0;
for(int i = 0; i < 30 && j < 10; i++) {
cShopItem item = active_shop.getItem(i);
if(item.type != eShopItemType::EMPTY)
univ.party.magic_store_items[which_shop][j++] = item.item;
}
for(int i = j; i < 10; i++)
univ.party.magic_store_items[which_shop][i].variety = eItemType::NO_ITEM;
}
}
void handle_shop_event(location p) {
@@ -251,16 +264,6 @@ void handle_sale(cShopItem item, int i) {
univ.party.alchemy[base_item.item_level] = true;
}
break;
case eShopItemType::FOOD:
// TODO: This shop type is never actually used. Should it be deleted or restored?
if(!take_gold(cost,false))
ASB("Not enough gold.");
else {
play_sound(-38);
ASB("You buy food.");
univ.party.food += base_item.item_level;
}
break;
case eShopItemType::HEAL_WOUNDS: case eShopItemType::REMOVE_CURSE: case eShopItemType::CURE_DUMBFOUNDING:
case eShopItemType::CURE_POISON: case eShopItemType::CURE_DISEASE: case eShopItemType::CURE_PARALYSIS:
case eShopItemType::DESTONE: case eShopItemType::RAISE_DEAD: case eShopItemType::RESURRECT:
@@ -376,7 +379,6 @@ void handle_info_request(cShopItem item) {
switch(item.type) {
case eShopItemType::EMPTY: break;
case eShopItemType::ITEM:
case eShopItemType::FOOD:
display_pc_item(6,0, base_item,0);
break;
case eShopItemType::ALCHEMY:
@@ -384,10 +386,10 @@ void handle_info_request(cShopItem item) {
display_alchemy();
break;
case eShopItemType::MAGE_SPELL:
display_spells(eSkill::MAGE_SPELLS,base_item.item_level - 800 - 30,0);
display_spells(eSkill::MAGE_SPELLS,base_item.item_level,0);
break;
case eShopItemType::PRIEST_SPELL:
display_spells(eSkill::PRIEST_SPELLS,base_item.item_level - 900 - 30,0);
display_spells(eSkill::PRIEST_SPELLS,base_item.item_level,0);
break;
case eShopItemType::SKILL:
display_skills(eSkill(base_item.item_level), nullptr);
@@ -430,11 +432,6 @@ void set_up_shop_array(eShopType store_shop_type, short store_shop_min, short st
if(univ.party[current_pc].main_status == eMainStatus::DUST)
active_shop.addSpecial(eShopItemType::RESURRECT);
break;
case eShopType::FOOD:
// TODO: This shop type is never actually used. Should it be deleted or resurrected?
for(int i = store_shop_min; i <= store_shop_max; i++)
active_shop.addSpecial(eShopItemType::FOOD, i);
break;
case eShopType::MAGIC_JUNK:
active_shop.addItems(univ.party.magic_store_items[0].begin(), univ.party.magic_store_items[0].end(), 1);
break;
@@ -646,6 +643,8 @@ void handle_talk_event(location p) {
oldstrnum1 = strnum1; oldstrnum2 = strnum2;
strnum1 = 40 + which_talk_entry * 2; strnum2 = 40 + which_talk_entry * 2 + 1;
eShopType shop;
switch(ttype) {
case eTalkNode::REGULAR:
break;
@@ -701,11 +700,6 @@ void handle_talk_event(location p) {
save_talk_str2 = "";
strnum2 = 0;
break;
case eTalkNode::BUY_ITEMS:
c = minmax(1,30,c);
start_shop_mode(eShopType::ITEMS,b,b + c - 1,a,save_talk_str1.c_str());
strnum1 = -1;
return;
case eTalkNode::TRAINING:
if((get_pc = char_select_pc(0,"Train who?")) < 6) {
strnum1 = -1;
@@ -714,26 +708,25 @@ void handle_talk_event(location p) {
save_talk_str1 = "You conclude your training.";
return;
case eTalkNode::BUY_MAGE:
c = minmax(1,61,c);
start_shop_mode(eShopType::MAGE,b,b + c - 1,a,save_talk_str1.c_str());
case eTalkNode::SHOP:
if(d < 0 || d > 11) {
giveError("Invalid shop type!");
return;
}
shop = eShopType(d);
switch(shop) {
case eShopType::ITEMS: case eShopType::MAGE: case eShopType::PRIEST:
c = minmax(1,30,c);
break;
case eShopType::ALCHEMY: c = minmax(1,20,c); break;
case eShopType::SKILLS: c = minmax(1,19,c); break;
case eShopType::HEALING: case eShopType::MAGIC_JUNK: case eShopType::MAGIC_LOUSY:
case eShopType::MAGIC_SO_SO: case eShopType::MAGIC_GOOD: case eShopType::MAGIC_GREAT:
break;
}
start_shop_mode(shop,b,b + c - 1,a,save_talk_str1.c_str());
strnum1 = -1;
return;
case eTalkNode::BUY_PRIEST:
c = minmax(1,61,c);
start_shop_mode(eShopType::PRIEST,b,b + c - 1,a,save_talk_str1.c_str());
strnum1 = -1;
return;
case eTalkNode::BUY_ALCHEMY:
c = minmax(1,19,c);
start_shop_mode(eShopType::ALCHEMY,b,b + c - 1,a,save_talk_str1.c_str());
strnum1 = -1;
return;
case eTalkNode::BUY_HEALING:
start_shop_mode(eShopType::HEALING,0,0,a,save_talk_str1.c_str());
strnum1 = -1;
return;
break;
case eTalkNode::SELL_WEAPONS:
strnum1 = -1;
stat_screen_mode = MODE_SELL_WEAP;
@@ -860,11 +853,6 @@ void handle_talk_event(location p) {
strnum2 = 0;
save_talk_str2 = "";
break;
case eTalkNode::BUY_JUNK:
start_shop_mode(eShopType(5 + b),0,
9,a,save_talk_str1.c_str());
strnum1 = -1;
return;
case eTalkNode::BUY_TOWN_LOC:
if(univ.party.can_find_town[b]) {
// TODO: Uh, is something supposed to happen here?

View File

@@ -710,7 +710,6 @@ void draw_shop_graphics(bool pressed,rectangle clip_area_rect) {
i = 0;
eShopType type = active_shop.getType();
if(type == eShopType::HEALING) i = 41;
else if(type == eShopType::FOOD) i = 42;
else if(type == eShopType::MAGE || type == eShopType::PRIEST || type == eShopType::ALCHEMY)
i = 43;
rectangle from_rect = {0,0,32,32};

View File

@@ -28,7 +28,6 @@ enum class eShopItemType {
MAGE_SPELL = 800,
PRIEST_SPELL = 900,
ALCHEMY = 500,
FOOD = 600,
SKILL = 1000,
};

View File

@@ -724,12 +724,8 @@ enum class eTalkNode {
DEP_ON_TIME = 4,
DEP_ON_TIME_AND_EVENT = 5,
DEP_ON_TOWN = 6,
BUY_ITEMS = 7,
SHOP = 7,
TRAINING = 8,
BUY_MAGE = 9,
BUY_PRIEST = 10,
BUY_ALCHEMY = 11,
BUY_HEALING = 12,
SELL_WEAPONS = 13,
SELL_ARMOR = 14,
SELL_ITEMS = 15,
@@ -740,7 +736,6 @@ enum class eTalkNode {
BUY_SHIP = 20,
BUY_HORSE = 21,
BUY_SPEC_ITEM = 22,
BUY_JUNK = 23,
BUY_TOWN_LOC = 24,
END_FORCE = 25,
END_FIGHT = 26,
@@ -1012,8 +1007,7 @@ enum class eShopType {
MAGIC_SO_SO = 7,
MAGIC_GOOD = 8,
MAGIC_GREAT = 9,
FOOD = 10,
SKILLS = 11,
SKILLS = 10,
};
enum class eAlchemy {

View File

@@ -50,23 +50,27 @@ void cSpeech::append(legacy::talking_record_type& old){
talk_nodes[i].type = eTalkNode::DEP_ON_TOWN;
break;
case 7: // Item shop
talk_nodes[i].type = eTalkNode::BUY_ITEMS;
talk_nodes[i].type = eTalkNode::SHOP;
talk_nodes[i].extras[3] = int(eShopType::ITEMS);
break;
case 8: // Training
talk_nodes[i].type = eTalkNode::TRAINING;
break;
case 9: // Spell shops
talk_nodes[i].type = eTalkNode::BUY_MAGE;
talk_nodes[i].extras[3] = int(eShopType::MAGE);
if(false) // Intentional fallthrough, but suppress first line
case 10:
talk_nodes[i].type = eTalkNode::BUY_PRIEST;
talk_nodes[i].extras[3] = int(eShopType::PRIEST);
talk_nodes[i].type = eTalkNode::SHOP;
talk_nodes[i].extras[1] += 30;
break;
case 11: // Alchemy shop
talk_nodes[i].type = eTalkNode::BUY_ALCHEMY;
talk_nodes[i].type = eTalkNode::SHOP;
talk_nodes[i].extras[3] = int(eShopType::ALCHEMY);
break;
case 12: // Healer
talk_nodes[i].type = eTalkNode::BUY_HEALING;
talk_nodes[i].type = eTalkNode::SHOP;
talk_nodes[i].extras[3] = int(eShopType::HEALING);
break;
case 13: // Sell weapons
talk_nodes[i].type = eTalkNode::SELL_WEAPONS;
@@ -99,7 +103,9 @@ void cSpeech::append(legacy::talking_record_type& old){
talk_nodes[i].type = eTalkNode::BUY_SPEC_ITEM;
break;
case 23: // Junk shop
talk_nodes[i].type = eTalkNode::BUY_JUNK;
talk_nodes[i].type = eTalkNode::SHOP;
talk_nodes[i].extras[3] = talk_nodes[i].extras[1] + int(eShopType::MAGIC_JUNK);
talk_nodes[i].extras[1] = 0;
break;
case 24: // buy town location
talk_nodes[i].type = eTalkNode::BUY_TOWN_LOC;

View File

@@ -711,7 +711,7 @@ static bool edit_spec_enc_value(cDialog& me, std::string item_hit, node_stack_t&
case eShopType::PRIEST: btn = 'P'; break;
case eShopType::ALCHEMY: btn = 'a'; break;
case eShopType::SKILLS: btn = 'k'; break;
case eShopType::FOOD: case eShopType::HEALING: case eShopType::MAGIC_GOOD: case eShopType::MAGIC_GREAT:
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;
}

View File

@@ -1038,6 +1038,7 @@ 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;
@@ -1056,9 +1057,22 @@ static bool save_talk_node(cDialog& me, std::string item_hit, std::stack<short>&
case eTalkNode::BUY_TOWN_LOC:
if(cre(talk_node.extras[1],0,scenario.towns.size(),"Town number must be from 0 to " + std::to_string(scenario.towns.size()) + ".","",&me)) return false;
break;
case eTalkNode::BUY_ITEMS: case eTalkNode::BUY_MAGE: case eTalkNode::BUY_PRIEST:
case eTalkNode::BUY_ALCHEMY: case eTalkNode::BUY_HEALING:
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;
}
break;
case eTalkNode::ENCHANT:
if(cre(talk_node.extras[0],0,6,"Enchantment type must be from 0 to 6. See the documentation for a list of possible abilities.","",&me)) return false;
@@ -1079,7 +1093,7 @@ static bool save_talk_node(cDialog& me, std::string item_hit, std::stack<short>&
case eTalkNode::CALL_SCEN_SPEC:
if(cre(talk_node.extras[0],-1,255,"The scenario special node called must be in the legal range (0 - 255), or -1 for No Special.","",&me)) return false;
break;
case eTalkNode::BUY_INFO: case eTalkNode::BUY_JUNK: case eTalkNode::DEP_ON_TIME: case eTalkNode::REGULAR:
case eTalkNode::BUY_INFO: case eTalkNode::DEP_ON_TIME: case eTalkNode::REGULAR:
case eTalkNode::END_ALARM: case eTalkNode::END_DIE: case eTalkNode::END_FIGHT: case eTalkNode::END_FORCE:
case eTalkNode::IDENTIFY: case eTalkNode::SELL_ARMOR: case eTalkNode::SELL_ITEMS: case eTalkNode::SELL_WEAPONS:
case eTalkNode::TRAINING:
@@ -1116,7 +1130,7 @@ static void put_talk_node_in_dlog(cDialog& me, std::stack<short>& talk_edit_stac
me["str1"].setText(talk_node.str1);
me["str2"].setText(talk_node.str2);
if(talk_node.type == eTalkNode::BUY_ITEMS || talk_node.type == eTalkNode::BUY_MAGE || talk_node.type == eTalkNode::BUY_PRIEST || talk_node.type == eTalkNode::BUY_ALCHEMY)
if(talk_node.type == eTalkNode::SHOP)
me["chooseB"].show();
else me["chooseB"].hide();
if(talk_node.type != eTalkNode::CALL_TOWN_SPEC && talk_node.type != eTalkNode::CALL_SCEN_SPEC)
@@ -1167,19 +1181,25 @@ 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.type) {
case eTalkNode::BUY_MAGE:
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 eTalkNode::BUY_PRIEST:
case int(eShopType::PRIEST):
i = choose_text(STRT_PRIEST,i,&me,"What is the first priest spell in the shop?");
break;
case eTalkNode::BUY_ALCHEMY:
case int(eShopType::ALCHEMY):
i = choose_text(STRT_ALCHEMY,i,&me,"What is the first recipe in the shop?");
break;
default:
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);
} else if(item_hit == "chooseA") {