Skip to content

Commit

Permalink
Put Gadget Shops back in business
Browse files Browse the repository at this point in the history
With compound changes to the game over time, the original reasoning for
removal of gadget shops in 024f7ef largely no longer applies, and we
have gained some reasons to reintroduce them:

- Now XP evokers stack it is definitely worth it to buy duplicates
- Talismans also now exist but jewellery shops seem like an odd place
  for them. Coglins desipte their evo aptitude can't even roll jewellery
  shops with Gozag!
- Wands still are in fairly abundant supply but for a character leaning
  heavily into evocations it is certainly possible to exhaust the
  nicer ones, so it's nice to have an option to top up charges
- Staves are their own object type so won't ever spawn in weapon shops:
  since they benefit from evocations now they actually fit well here.
- Orbs are awkwardly placed in an armour shop as they are not defensive
  items, they just happen to be carried in the shield slot. It's not
  hard to conceive they belong in a gadget shop (even though they're
  the only item here that has nothing to do with Evocations).

So, the new gadget shop has actually quite a nice varied object pool
to draw from now, mostly tied together with an evocations theme, but
interesting to a few different types of character.

It's a very nice shop if you're after such things, and consequently
won't be seen before D:10 or Orc:$ - unless you're lucky with a vault,
or worship Gozag.
  • Loading branch information
chucksellick committed Jan 29, 2025
1 parent 62b7471 commit 2bcf91c
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 27 deletions.
1 change: 1 addition & 0 deletions crawl-ref/source/dgn-overview.cc
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ static string shoptype_to_string(shop_type s)
case SHOP_BOOK: return "<w>:</w>";
case SHOP_DISTILLERY: return "<w>!</w>";
case SHOP_SCROLL: return "<w>?</w>";
case SHOP_EVOKABLES: return "<w>}</w>";
default: return "<w>x</w>";
}
}
Expand Down
71 changes: 53 additions & 18 deletions crawl-ref/source/dungeon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5995,7 +5995,7 @@ void place_spec_shop(const coord_def& where, shop_type force_type)

int greed_for_shop_type(shop_type shop, int level_number)
{
if (!shoptype_identifies_stock(shop))
if (!shoptype_identifies_stock(shop) || shop == SHOP_EVOKABLES)
{
const int rand = random2avg(19, 2);
return 15 + rand + random2(level_number);
Expand Down Expand Up @@ -6090,6 +6090,22 @@ static int _choose_shop_item_level(shop_type shop_type_, int level_number)
return min(base_level + bazaar_bonus, level_number * 5);
}

static int _item_in_shop_subtype(shop_type shop_type,
object_class_type base_type)
{
switch (shop_type)
{
// We'd almost never roll an orb from the armour subtypes so force it here
case SHOP_EVOKABLES:
if (base_type == OBJ_ARMOUR)
return ARM_ORB;
// All other shops and base types are happy with any sub type
// (orbs will be evicted from armour shops later in _valid_item_for_shop)
default:
return OBJ_RANDOM;
}
}

/**
* Is the given item valid for placement in the given shop?
*
Expand All @@ -6098,8 +6114,7 @@ static int _choose_shop_item_level(shop_type shop_type_, int level_number)
* @param spec The specification for the shop.
* @return Whether the item is valid.
*/
static bool _valid_item_for_shop(int item_index, shop_type shop_type_,
shop_spec &spec)
static bool _valid_item_for_shop(int item_index, shop_type shop_type_)
{
if (item_index == NON_ITEM)
return false;
Expand All @@ -6113,15 +6128,15 @@ static bool _valid_item_for_shop(int item_index, shop_type shop_type_,
return false;

// Don't place missiles or books in general antique shops...
if (shop_type_ == SHOP_GENERAL_ANTIQUE
&& (item.base_type == OBJ_MISSILES
|| item.base_type == OBJ_BOOKS))
{
// ...unless they're specified by the item spec.
return !spec.items.empty();
}

return true;
return !(shop_type_ == SHOP_GENERAL_ANTIQUE
&& (item.base_type == OBJ_MISSILES
|| item.base_type == OBJ_BOOKS)
// Orb "shields" aren't really armour
|| (shop_type_ == SHOP_ARMOUR || shop_type_ == SHOP_ARMOUR_ANTIQUE)
&& item.sub_type == ARM_ORB
// But instead count as gadgets
|| (shop_type_ == SHOP_EVOKABLES)
&& item.base_type == OBJ_ARMOUR && item.sub_type != ARM_ORB);
}

/**
Expand Down Expand Up @@ -6155,7 +6170,6 @@ static void _stock_shop_item(int j, shop_type shop_type_,
while (true)
{
object_class_type basetype = item_in_shop(shop_type_);
int subtype = OBJ_RANDOM;

if (!spec.items.empty() && !spec.use_all)
{
Expand All @@ -6176,6 +6190,7 @@ static void _stock_shop_item(int j, shop_type shop_type_,
// gozag shop items are better
const bool good_item = spec.gozag || one_chance_in(4);
const int level = good_item ? ISPEC_GOOD_ITEM : item_level;
const int subtype = _item_in_shop_subtype(shop_type_, basetype);
item_index = items(true, basetype, subtype, level);
}

Expand All @@ -6190,8 +6205,12 @@ static void _stock_shop_item(int j, shop_type shop_type_,
}
}

if (_valid_item_for_shop(item_index, shop_type_, spec))
// Exit loop if we found a valid item or one from the item spec
if (item_index != NON_ITEM && !spec.items.empty()
|| _valid_item_for_shop(item_index, shop_type_))
{
break;
}

// Reset object and try again.
if (item_index != NON_ITEM)
Expand Down Expand Up @@ -6224,11 +6243,10 @@ static shop_type _random_shop()
{
return random_choose(SHOP_WEAPON, SHOP_ARMOUR, SHOP_WEAPON_ANTIQUE,
SHOP_ARMOUR_ANTIQUE, SHOP_GENERAL_ANTIQUE,
SHOP_JEWELLERY, SHOP_BOOK,
SHOP_JEWELLERY, SHOP_BOOK, SHOP_EVOKABLES,
SHOP_DISTILLERY, SHOP_SCROLL, SHOP_GENERAL);
}


/**
* Attempt to place a shop in a given location.
*
Expand Down Expand Up @@ -6256,6 +6274,11 @@ void place_spec_shop(const coord_def& where, shop_spec &spec, int shop_level)
shop.type = spec.sh_type;
if (shop.type == SHOP_RANDOM)
shop.type = _random_shop();
// Re-roll if we got a gadget shop below level 10; you need to roll it
// twice in a row to get one here. They're *really* rare. Level 10 means
// they could start showing up in Orc end vaults.
if (shop.type == SHOP_EVOKABLES && level_number < 10)
shop.type = _random_shop();
shop.greed = _shop_greed(shop.type, level_number, spec.greed);
shop.pos = where;

Expand Down Expand Up @@ -6292,8 +6315,6 @@ object_class_type item_in_shop(shop_type shop_type)
return OBJ_RANDOM;

case SHOP_JEWELLERY:
if (one_chance_in(10))
return OBJ_TALISMANS;
return OBJ_JEWELLERY;

case SHOP_BOOK:
Expand All @@ -6305,6 +6326,20 @@ object_class_type item_in_shop(shop_type shop_type)
case SHOP_SCROLL:
return OBJ_SCROLLS;

case SHOP_EVOKABLES:
{
// Gadget shops are rather a grab bag of stuff that doesn't fit elsewhere.
// They are very nice shops so are quite rare.
return random_choose_weighted(
100, OBJ_WANDS,
50, OBJ_MISCELLANY,
30, OBJ_TALISMANS, // Evoked even if they don't use evo
20, OBJ_STAVES, // Since they use evocations skill too
// (and aren't covered by OBJ_WEAPONS)
20, OBJ_ARMOUR // Only for orbs
);
}

default:
die("unknown shop type %d", shop_type);
}
Expand Down
3 changes: 2 additions & 1 deletion crawl-ref/source/god-abil.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3662,9 +3662,10 @@ static bool _shop_type_valid(shop_type type)
{
#if TAG_MAJOR_VERSION == 34
case SHOP_FOOD:
case SHOP_EVOKABLES:
return false;
#endif
case SHOP_EVOKABLES:
return !you.has_mutation(MUT_NO_ARTIFICE);
case SHOP_DISTILLERY:
return !you.has_mutation(MUT_NO_DRINK);
case SHOP_WEAPON:
Expand Down
4 changes: 1 addition & 3 deletions crawl-ref/source/shop-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ enum shop_type
SHOP_ARMOUR_ANTIQUE,
SHOP_GENERAL_ANTIQUE,
SHOP_JEWELLERY,
#if TAG_MAJOR_VERSION == 34
SHOP_EVOKABLES, // wands, rods, and misc items
#endif
SHOP_EVOKABLES, // wands, evokers, and other miscellany
SHOP_BOOK,
#if TAG_MAJOR_VERSION == 34
SHOP_FOOD,
Expand Down
6 changes: 2 additions & 4 deletions crawl-ref/source/shopping.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1535,9 +1535,9 @@ string shop_type_name(shop_type type)
return "Jewellery";
case SHOP_BOOK:
return "Book";
#if TAG_MAJOR_VERSION == 34
case SHOP_EVOKABLES:
return "Gadget";
#if TAG_MAJOR_VERSION == 34
case SHOP_FOOD:
return "Removed Food";
#endif
Expand Down Expand Up @@ -1639,9 +1639,7 @@ static const char *shop_types[] =
"antique armour",
"antiques",
"jewellery",
#if TAG_MAJOR_VERSION == 34
"removed gadget",
#endif
"gadget",
"book",
#if TAG_MAJOR_VERSION == 34
"removed food",
Expand Down
2 changes: 1 addition & 1 deletion crawl-ref/source/tilepick.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ tileidx_t tileidx_shop(const shop_struct *shop)
return TILE_SHOP_ARMOUR;
case SHOP_JEWELLERY:
return TILE_SHOP_JEWELLERY;
#if TAG_MAJOR_VERSION == 34
case SHOP_EVOKABLES:
return TILE_SHOP_GADGETS;
#if TAG_MAJOR_VERSION == 34
case SHOP_FOOD:
return TILE_SHOP_FOOD;
#endif
Expand Down

0 comments on commit 2bcf91c

Please sign in to comment.