Refactor some shop code

This commit is contained in:
Fabian Dill 2020-08-25 14:31:20 +02:00
parent a39459e9fc
commit 6d38e87527
4 changed files with 54 additions and 32 deletions

View File

@ -1056,38 +1056,39 @@ class ShopType(Enum):
TakeAny = 1
UpgradeShop = 2
class Shop(object):
slots = 3
class Shop():
slots = 3 # slot count is not dynamic in asm, however inventory can have None as empty slots
blacklist = set() # items that don't work, todo: actually check against this
type = ShopType.Shop
def __init__(self, region, room_id, type, shopkeeper_config, custom, locked: bool):
def __init__(self, region: Region, room_id: int, shopkeeper_config: int, custom: bool, locked: bool):
self.region = region
self.room_id = room_id
self.type = type
self.inventory: List[Union[None, dict]] = [None, None, None]
self.inventory: List[Union[None, dict]] = [None] * self.slots
self.shopkeeper_config = shopkeeper_config
self.custom = custom
self.locked = locked
@property
def item_count(self):
return (3 if self.inventory[2] else
2 if self.inventory[1] else
1 if self.inventory[0] else
0)
def item_count(self) -> int:
for x in range(self.slots - 1, -1, -1): # last x is 0
if self.inventory[x]:
return x + 1
return 0
def get_bytes(self):
def get_bytes(self) -> List[int]:
# [id][roomID-low][roomID-high][doorID][zero][shop_config][shopkeeper_config][sram_index]
entrances = self.region.entrances
config = self.item_count
if len(entrances) == 1 and entrances[0].name in door_addresses:
door_id = door_addresses[entrances[0].name][0]+1
door_id = door_addresses[entrances[0].name][0] + 1
else:
door_id = 0
config |= 0x40 # ignore door id
config |= 0x40 # ignore door id
if self.type == ShopType.TakeAny:
config |= 0x80
if self.type == ShopType.UpgradeShop:
config |= 0x10 # Alt. VRAM
elif self.type == ShopType.UpgradeShop:
config |= 0x10 # Alt. VRAM
return [0x00]+int16_as_bytes(self.room_id)+[door_id, 0x00, config, self.shopkeeper_config, 0x00]
def has_unlimited(self, item: str) -> bool:
@ -1111,7 +1112,7 @@ class Shop(object):
return False
def clear_inventory(self):
self.inventory = [None, None, None]
self.inventory = [None] * self.slots
def add_inventory(self, slot: int, item: str, price: int, max: int = 0,
replacement: Optional[str] = None, replacement_price: int = 0, create_location: bool = False):
@ -1137,8 +1138,21 @@ class Shop(object):
'create_location': self.inventory[slot]["create_location"]
}
class TakeAny(Shop):
type = ShopType.TakeAny
class UpgradeShop(Shop):
type = ShopType.UpgradeShop
# Potions break due to VRAM flags set in UpgradeShop.
# Didn't check for more things breaking as not much else can be shuffled here currently
blacklist = item_name_groups["Potions"]
class Spoiler(object):
world: World
def __init__(self, world):
self.world = world
self.hashes = {}

View File

@ -1,7 +1,7 @@
from collections import namedtuple
import logging
from BaseClasses import Region, RegionType, Shop, ShopType, Location
from BaseClasses import Region, RegionType, ShopType, Location, TakeAny
from Bosses import place_bosses
from Dungeons import get_dungeon_item_pool
from EntranceShuffle import connect_entrance
@ -350,27 +350,31 @@ def shuffle_shops(world, items, player: int):
if 'p' in option or 'i' in option:
shops = []
upgrade_shops = []
total_inventory = []
for shop in world.shops:
if shop.type == ShopType.Shop and shop.region.player == player and shop.region.name != 'Potion Shop':
shops.append(shop)
total_inventory.extend(shop.inventory)
if shop.region.player == player:
if shop.type == ShopType.UpgradeShop:
upgrade_shops.append(shop)
elif shop.type == ShopType.Shop and shop.region.name != 'Potion Shop':
shops.append(shop)
total_inventory.extend(shop.inventory)
if 'p' in option:
def price_adjust(price: int) -> int:
# it is important that a base price of 0 always returns 0 as new price!
return int(price * (0.5 + world.random.random() * 2))
for item in total_inventory:
def adjust_item(item):
if item:
item["price"] = price_adjust(item["price"])
item['replacement_price'] = price_adjust(item["price"])
for shop in world.shops:
if shop.type == ShopType.UpgradeShop and shop.region.player == player:
for item in shop.inventory:
if item:
item['price'] = price_adjust(item["price"])
item['replacement_price'] = price_adjust(item["price"])
for item in total_inventory:
adjust_item(item)
for shop in upgrade_shops:
for item in shop.inventory:
adjust_item(item)
if 'i' in option:
world.random.shuffle(total_inventory)
@ -405,7 +409,7 @@ def set_up_take_anys(world, player):
entrance = world.get_region(reg, player).entrances[0]
connect_entrance(world, entrance.name, old_man_take_any.name, player)
entrance.target = 0x58
old_man_take_any.shop = Shop(old_man_take_any, 0x0112, ShopType.TakeAny, 0xE2, True, True)
old_man_take_any.shop = TakeAny(old_man_take_any, 0x0112, 0xE2, True, True)
world.shops.append(old_man_take_any.shop)
swords = [item for item in world.itempool if item.type == 'Sword' and item.player == player]
@ -427,7 +431,7 @@ def set_up_take_anys(world, player):
entrance = world.get_region(reg, player).entrances[0]
connect_entrance(world, entrance.name, take_any.name, player)
entrance.target = target
take_any.shop = Shop(take_any, room_id, ShopType.TakeAny, 0xE3, True, True)
take_any.shop = TakeAny(take_any, room_id, 0xE3, True, True)
world.shops.append(take_any.shop)
take_any.shop.add_inventory(0, 'Blue Potion', 0, 0)
take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0)

View File

@ -425,7 +425,8 @@ def copy_dynamic_regions_and_locations(world, ret):
# Note: ideally exits should be copied here, but the current use case (Take anys) do not require this
if region.shop:
new_reg.shop = Shop(new_reg, region.shop.room_id, region.shop.type, region.shop.shopkeeper_config, region.shop.custom, region.shop.locked)
new_reg.shop = region.shop.__class__(new_reg, region.shop.room_id, region.shop.shopkeeper_config,
region.shop.custom, region.shop.locked)
ret.shops.append(new_reg.shop)
for location in world.dynamic_locations:

View File

@ -1,6 +1,6 @@
import collections
from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType
from BaseClasses import Region, Location, Entrance, RegionType, Shop, TakeAny, UpgradeShop, ShopType
def create_regions(world, player):
@ -365,12 +365,15 @@ def mark_light_world_regions(world, player: int):
def create_shops(world, player: int):
cls_mapping = {ShopType.UpgradeShop: UpgradeShop,
ShopType.Shop: Shop,
ShopType.TakeAny: TakeAny}
for region_name, (room_id, type, shopkeeper, custom, locked, inventory) in shop_table.items():
if world.mode[player] == 'inverted' and region_name == 'Dark Lake Hylia Shop':
locked = True
inventory = [('Blue Potion', 160), ('Blue Shield', 50), ('Bombs (10)', 50)]
region = world.get_region(region_name, player)
shop = Shop(region, room_id, type, shopkeeper, custom, locked)
shop = cls_mapping[type](region, room_id, shopkeeper, custom, locked)
region.shop = shop
world.shops.append(shop)
for index, item in enumerate(inventory):