Skip to content

Commit

Permalink
A new Shop Name generator
Browse files Browse the repository at this point in the history
  • Loading branch information
chucksellick committed May 29, 2024
1 parent b850005 commit e42e1eb
Show file tree
Hide file tree
Showing 12 changed files with 1,970 additions and 77 deletions.
1,788 changes: 1,788 additions & 0 deletions crawl-ref/source/dat/database/shopname.txt

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions crawl-ref/source/database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ static TextDB AllDBs[] =
"montitle.txt", // titles for monsters (i.e. uniques)
"decorlines.txt", // miscellaneous lines for walking on decoration
"gizmo.txt", // name-assembling for gizmos
"shopname.txt", // fancy shop name generator
}),

TextDB("quotes", "descript/",
Expand Down Expand Up @@ -700,7 +701,6 @@ static void _call_recursive_replacement(string &str, TextDB &db,
break;
}

string marker_full = str.substr(pos, end - pos + 1);
string marker = str.substr(pos + 1, end - pos - 1);

string replacement =
Expand All @@ -714,7 +714,7 @@ static void _call_recursive_replacement(string &str, TextDB &db,
}
else
{
str.replace(pos, marker_full.length(), replacement);
str.replace(pos, marker.length() + 2, replacement);

// Start search from pos rather than end + 1, so that if
// the replacement contains its own @foo@, we can replace
Expand Down
26 changes: 17 additions & 9 deletions crawl-ref/source/dungeon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6236,16 +6236,24 @@ void place_spec_shop(const coord_def& where, shop_spec &spec, int shop_level)
shop_struct& shop = env.shop[where];

const int level_number = shop_level ? shop_level : env.absdepth0;

for (int j = 0; j < 3; j++)
shop.keeper_name[j] = 1 + random2(200);
shop.shop_name = spec.name;
shop.shop_type_name = spec.type;
shop.shop_suffix_name = spec.suffix;
shop.type = spec.sh_type == SHOP_RANDOM ? _random_shop() : spec.sh_type;
shop.level = level_number * 2;
shop.type = spec.sh_type;
if (shop.type == SHOP_RANDOM)
shop.type = _random_shop();
if (!spec.full_name.empty())
{
shop.shop_name = spec.name;
shop.full_shop_name = spec.full_name;
}
else if (spec.name.empty() && spec.type.empty() && spec.suffix.empty())
{
auto names = generate_shop_name(shop.type, shop.level, spec.gozag);
std::tie(shop.full_shop_name, shop.shop_name) = names;
}
else
{
shop.shop_name = spec.name;
shop.shop_type_name = spec.type;
shop.shop_suffix_name = spec.suffix;
}
shop.greed = _shop_greed(shop.type, level_number, spec.greed);
shop.pos = where;

Expand Down
61 changes: 22 additions & 39 deletions crawl-ref/source/god-abil.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3144,6 +3144,7 @@ static int _gozag_shop_price(int index)
static void _setup_gozag_shop(int index, vector<shop_type> &valid_shops)
{
ASSERT(!you.props.exists(make_stringf(GOZAG_SHOPKEEPER_NAME_KEY, index)));
ASSERT(!you.props.exists(make_stringf(GOZAG_SHOP_NAME_KEY, index)));

shop_type type = NUM_SHOPS;
int choice = random2(valid_shops.size());
Expand All @@ -3152,17 +3153,11 @@ static void _setup_gozag_shop(int index, vector<shop_type> &valid_shops)
valid_shops.erase(valid_shops.begin() + choice);
you.props[make_stringf(GOZAG_SHOP_TYPE_KEY, index)].get_int() = type;

you.props[make_stringf(GOZAG_SHOPKEEPER_NAME_KEY, index)].get_string()
= make_name();

const bool need_suffix = type != SHOP_GENERAL
&& type != SHOP_GENERAL_ANTIQUE
&& type != SHOP_DISTILLERY;
you.props[make_stringf(GOZAG_SHOP_SUFFIX_KEY, index)].get_string()
= need_suffix
? random_choose("Shoppe", "Boutique",
"Emporium", "Shop")
: "";
auto names = generate_shop_name(type, you.experience_level, true);
you.props[make_stringf(GOZAG_SHOP_NAME_KEY, index)]
= names.first;
you.props[make_stringf(GOZAG_SHOPKEEPER_NAME_KEY, index)]
= names.second;

you.props[make_stringf(GOZAG_SHOP_COST_KEY, index)].get_int()
= gozag_price_for_shop();
Expand All @@ -3181,20 +3176,18 @@ static string _describe_gozag_shop(int index)
const int cost = _gozag_shop_price(index);

const char offer_letter = 'a' + index;

const string shop_name =
apostrophise(you.props[make_stringf(GOZAG_SHOPKEEPER_NAME_KEY,
index)].get_string());
you.props[make_stringf(GOZAG_SHOP_NAME_KEY,
index)].get_string();
const shop_type type = _gozag_shop_type(index);
const string type_name = shop_type_name(type);
const string suffix =
you.props[make_stringf(GOZAG_SHOP_SUFFIX_KEY, index)].get_string();

return make_stringf(" [%c] %5d gold - %s %s %s",
return make_stringf(" [%c] %5d gold - %s (%s)",
offer_letter,
cost,
shop_name.c_str(),
type_name.c_str(),
suffix.c_str());
type_name.c_str());
}

/**
Expand Down Expand Up @@ -3230,24 +3223,15 @@ static int _gozag_choose_shop()
/**
* Make a vault spec for the gozag shop offer at the given index.
*/
static string _gozag_shop_spec(int index)
static shop_spec _gozag_shop_spec(int index)
{
const shop_type type = _gozag_shop_type(index);
const string name =
you.props[make_stringf(GOZAG_SHOPKEEPER_NAME_KEY, index)];
const string full_name =
you.props[make_stringf(GOZAG_SHOP_NAME_KEY, index)];

string suffix = replace_all(
you.props[make_stringf(GOZAG_SHOP_SUFFIX_KEY,
index)]
.get_string(), " ", "_");
if (!suffix.empty())
suffix = " suffix:" + suffix;

return make_stringf("%s shop name:%s%s gozag",
shoptype_to_str(type),
replace_all(name, " ", "_").c_str(),
suffix.c_str());

return shop_spec(type, name, "", "", -1, -1, false, true, full_name);
}

/**
Expand All @@ -3258,13 +3242,8 @@ static string _gozag_shop_spec(int index)
static void _gozag_place_shop(int index)
{
ASSERT(env.grid(you.pos()) == DNGN_FLOOR);
keyed_mapspec kmspec;
kmspec.set_feat(_gozag_shop_spec(index), false);

feature_spec feat = kmspec.get_feat();
if (!feat.shop)
die("Invalid shop spec?");
place_spec_shop(you.pos(), *feat.shop, you.experience_level);
shop_spec spec = _gozag_shop_spec(index);
place_spec_shop(you.pos(), spec, you.experience_level);

link_items();
env.markers.add(new map_feature_marker(you.pos(), DNGN_ABANDONED_SHOP));
Expand All @@ -3290,6 +3269,7 @@ static bool _shop_type_valid(shop_type type)
{
#if TAG_MAJOR_VERSION == 34
case SHOP_FOOD:
return false;
#endif
case SHOP_EVOKABLES:
return !you.has_mutation(MUT_NO_ARTIFICE);
Expand Down Expand Up @@ -3347,9 +3327,12 @@ bool gozag_call_merchant()

for (int j = 0; j < GOZAG_MAX_SHOPS; j++)
{
#if TAG_MAJOR_VERSION == 34
you.props.erase(make_stringf(GOZAG_SHOPKEEPER_NAME_KEY, j));
you.props.erase(make_stringf(GOZAG_SHOP_TYPE_KEY, j));
you.props.erase(make_stringf(GOZAG_SHOP_SUFFIX_KEY, j));
#endif
you.props.erase(make_stringf(GOZAG_SHOP_NAME_KEY, j));
you.props.erase(make_stringf(GOZAG_SHOP_TYPE_KEY, j));
you.props.erase(make_stringf(GOZAG_SHOP_COST_KEY, j));
}

Expand Down
5 changes: 4 additions & 1 deletion crawl-ref/source/god-abil.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ const char * const GOZAG_POTIONS_KEY = "gozag_potions%d";
const char * const GOZAG_PRICE_KEY = "gozag_price%d";

const char * const GOZAG_SHOPKEEPER_NAME_KEY = "gozag_shopkeeper_%d";
const char * const GOZAG_SHOP_TYPE_KEY = "gozag_shop_type_%d";
#if TAG_MAJOR_VERSION == 34
const char * const GOZAG_SHOP_SUFFIX_KEY = "gozag_shop_suffix_%d";
#endif
const char * const GOZAG_SHOP_NAME_KEY = "gozag_shop_%d";
const char * const GOZAG_SHOP_TYPE_KEY = "gozag_shop_type_%d";
const char * const GOZAG_SHOP_COST_KEY = "gozag_shop_cost_%d";

#define GOZAG_GOLD_AURA_KEY "gozag_gold_aura_amount"
Expand Down
3 changes: 2 additions & 1 deletion crawl-ref/source/mapdef.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6083,6 +6083,7 @@ feature_spec keyed_mapspec::parse_shop(string s, int weight, int mimic,

const bool gozag = strip_tag(s, "gozag");

string full_shop_name = replace_all_of(strip_tag_prefix(s, "full_name:"), "_", " ");
string shop_name = replace_all_of(strip_tag_prefix(s, "name:"), "_", " ");
string shop_type_name = replace_all_of(strip_tag_prefix(s, "type:"),
"_", " ");
Expand Down Expand Up @@ -6123,7 +6124,7 @@ feature_spec keyed_mapspec::parse_shop(string s, int weight, int mimic,
feature_spec fspec(-1, weight, mimic, no_mimic);
fspec.shop.reset(new shop_spec(shop, shop_name, shop_type_name,
shop_suffix_name, greed,
num_items, use_all, gozag));
num_items, use_all, gozag, full_shop_name));
fspec.shop->items = items;
return fspec;
}
Expand Down
8 changes: 6 additions & 2 deletions crawl-ref/source/mapdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -797,10 +797,14 @@ struct shop_spec
* stock).
* */

string full_name; /**< Overrides all other name parameters and sets the entire
name of the shop */

shop_spec(shop_type sh, string n="", string t="",
string s="", int g=-1, int ni=-1, bool u=false, bool goz=false)
string s="", int g=-1, int ni=-1, bool u=false, bool goz=false,
string fname="")
: sh_type(sh), name(n), type(t), suffix(s),
greed(g), num_items(ni), items(), use_all(u), gozag(goz) { }
greed(g), num_items(ni), items(), use_all(u), gozag(goz), full_name(fname) { }
};

/**
Expand Down
100 changes: 90 additions & 10 deletions crawl-ref/source/shopping.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "travel.h"
#include "unicode.h"
#include "unwind.h"
#include "database.h"

ShoppingList shopping_list;

Expand Down Expand Up @@ -1545,27 +1546,106 @@ static const char *_shop_type_suffix(shop_type type, const coord_def &where)
return suffixnames[(where.x + where.y) % ARRAYSZ(suffixnames)];
}

string generate_shopkeeper_name(bool fancy, bool gozag)
{
// Plain shops always use traditional name generator (same as Pan Lords),
// still a chance for fancy shops though (they will get further flowery
// language in the main generator anyway).
// if (!(gozag || fancy) || one_chance_in(3))
// return make_name();

string key = (gozag ? "gozag" : fancy ? "fancy" : "plain")
+ string(" shopkeeper full name");
auto generated = getMiscString(key);
string name = maybe_pick_random_substring(generated);

// They can *still* use classic name but with a prefix/suffix added e.g.
// "Brave [Name]" or "[Name] the Brave".
if (name.find("@Classic_name@") != string::npos)
name = replace_all(name, "@Classic_name@", make_name());

return name;
}

pair<string, string> generate_shop_name(shop_type type, int shop_level, bool gozag)
{
// Generate an appropriately long name for the level. Level is (2 * (absdepth || XL))
// depending on how it was generated, so it can get up to 54 with Gozag.
// We can generate some pretty succinct names so let's start at 16 and go up to 70
// at max (any more than this would cause display issues anyway).
const uint target_max = 20 + shop_level;
const uint target_min = 20 - shop_level / 5;
const size_t target_length = random_range(target_min, target_max);
const bool fancy = shop_level >= 20; // 20 for orc, 25 for later...
// string key = (gozag ? "gozag " : fancy ? "fancy " : "")
// + shop_type_name(type) + " shop";
string key = shop_type_name(type) + " shop name";
auto best = make_pair(string(""), string(""));
size_t best_name_size = 0;
// Have 10 tries, and veto the lengths we don't like. Aiming to get the
// closest to the desired length without actually being longer.
for (int n=0; n<10; n++)
{
auto keeper_name = generate_shopkeeper_name(fancy, gozag);
auto generated = getMiscString(key);
if (generated.empty())
{
mprf("Empty shopkeeper name! Key: %s", key.c_str());
continue;
}
string name = maybe_pick_random_substring(generated);
name = replace_all(name, "@The_shopkeeper's@", apostrophise(keeper_name));
name = replace_all(name, "@The_shopkeeper@", keeper_name);
mprf("%s Target: %lu Found: %lu Keeper: %s Name: %s", name.c_str(), target_length, name.size(), keeper_name.c_str(), generated.c_str());

if (name.size() == target_length)
{
// Exact length, can't do any better, stop here
best = make_pair(name, keeper_name);
break;
}
else if (name.size() > target_length)
{
// Keep the name if it's the first or it's shorter than the best
// we found so far
if (best_name_size == 0 || name.size() < best_name_size)
best = make_pair(name, keeper_name);
}
// Name is within target but longer than best so prefer this one
else if (name.size() > best_name_size)
best = make_pair(name, keeper_name);
best_name_size = best.first.size();
}

mprf("Final: %s", best.first.c_str());

return best;
}

/**
* Old name builder for shops. Still used for most vault-defined shops
* as I don't want to mess with their creation when they've overriden
* shopkeeper names, shop type names, etc.
*
* If a name has been set from the new generator during shop placement,
* we'll just return that instead.
*/
string shop_name(const shop_struct& shop)
{
const shop_type type = shop.type;

// New builder populates this string so just return it
if (!shop.full_shop_name.empty())
return shop.full_shop_name;

string sh_name = "";

#if TAG_MAJOR_VERSION == 34
// xref ShopInfo::load
if (shop.shop_name == " ")
return shop.shop_type_name;
#endif
if (!shop.shop_name.empty())
sh_name += apostrophise(shop.shop_name) + " ";
else
{
uint32_t seed = static_cast<uint32_t>(shop.keeper_name[0])
| (static_cast<uint32_t>(shop.keeper_name[1]) << 8)
| (static_cast<uint32_t>(shop.keeper_name[1]) << 16);

sh_name += apostrophise(make_name(seed)) + " ";
}
sh_name += apostrophise(shop.shop_name) + " ";

if (!shop.shop_type_name.empty())
sh_name += shop.shop_type_name;
Expand Down
6 changes: 5 additions & 1 deletion crawl-ref/source/shopping.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ shop_struct *shop_at(const coord_def& where);

void destroy_shop_at(coord_def p);

string generate_shopkeeper_name(bool fancy, bool gozag);
pair<string,string> generate_shop_name(const shop_type type, int shop_level,
bool gozag);
string shop_name(const shop_struct& shop);
string shop_type_name(shop_type type);

Expand All @@ -60,7 +63,8 @@ struct shop_struct
string shop_type_name;
string shop_suffix_name;

FixedVector<uint8_t, 3> keeper_name;
// Overrides name, type, suffix, and just specifies entire shop name
string full_shop_name;

vector<item_def> stock;
#if TAG_MAJOR_VERSION == 34
Expand Down
1 change: 1 addition & 0 deletions crawl-ref/source/tag-version.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ enum tag_minor_version
TAG_MINOR_TALISMANS_SEEN, // Keep track of seen talismans
TAG_MINOR_FIX_APOSTLE_DAMAGE, // Fix damage tracking of banished apostles
TAG_MINOR_MON_AURA_REFACTORING,// Mark enchantments from passive auras in mon_enchant
TAG_MINOR_SHOP_NAME_GENERATOR, // New generator for shop names
#endif
NUM_TAG_MINORS,
TAG_MINOR_VERSION = NUM_TAG_MINORS - 1
Expand Down
Loading

0 comments on commit e42e1eb

Please sign in to comment.