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 TakeAny = 1
UpgradeShop = 2 UpgradeShop = 2
class Shop(object): class Shop():
slots = 3 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.region = region
self.room_id = room_id self.room_id = room_id
self.type = type self.inventory: List[Union[None, dict]] = [None] * self.slots
self.inventory: List[Union[None, dict]] = [None, None, None]
self.shopkeeper_config = shopkeeper_config self.shopkeeper_config = shopkeeper_config
self.custom = custom self.custom = custom
self.locked = locked self.locked = locked
@property @property
def item_count(self): def item_count(self) -> int:
return (3 if self.inventory[2] else for x in range(self.slots - 1, -1, -1): # last x is 0
2 if self.inventory[1] else if self.inventory[x]:
1 if self.inventory[0] else return x + 1
0) 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] # [id][roomID-low][roomID-high][doorID][zero][shop_config][shopkeeper_config][sram_index]
entrances = self.region.entrances entrances = self.region.entrances
config = self.item_count config = self.item_count
if len(entrances) == 1 and entrances[0].name in door_addresses: 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: else:
door_id = 0 door_id = 0
config |= 0x40 # ignore door id config |= 0x40 # ignore door id
if self.type == ShopType.TakeAny: if self.type == ShopType.TakeAny:
config |= 0x80 config |= 0x80
if self.type == ShopType.UpgradeShop: elif self.type == ShopType.UpgradeShop:
config |= 0x10 # Alt. VRAM config |= 0x10 # Alt. VRAM
return [0x00]+int16_as_bytes(self.room_id)+[door_id, 0x00, config, self.shopkeeper_config, 0x00] return [0x00]+int16_as_bytes(self.room_id)+[door_id, 0x00, config, self.shopkeeper_config, 0x00]
def has_unlimited(self, item: str) -> bool: def has_unlimited(self, item: str) -> bool:
@ -1111,7 +1112,7 @@ class Shop(object):
return False return False
def clear_inventory(self): 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, 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): 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"] '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): class Spoiler(object):
world: World world: World
def __init__(self, world): def __init__(self, world):
self.world = world self.world = world
self.hashes = {} self.hashes = {}

View File

@ -1,7 +1,7 @@
from collections import namedtuple from collections import namedtuple
import logging import logging
from BaseClasses import Region, RegionType, Shop, ShopType, Location from BaseClasses import Region, RegionType, ShopType, Location, TakeAny
from Bosses import place_bosses from Bosses import place_bosses
from Dungeons import get_dungeon_item_pool from Dungeons import get_dungeon_item_pool
from EntranceShuffle import connect_entrance from EntranceShuffle import connect_entrance
@ -350,27 +350,31 @@ def shuffle_shops(world, items, player: int):
if 'p' in option or 'i' in option: if 'p' in option or 'i' in option:
shops = [] shops = []
upgrade_shops = []
total_inventory = [] total_inventory = []
for shop in world.shops: for shop in world.shops:
if shop.type == ShopType.Shop and shop.region.player == player and shop.region.name != 'Potion Shop': if shop.region.player == player:
shops.append(shop) if shop.type == ShopType.UpgradeShop:
total_inventory.extend(shop.inventory) 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: if 'p' in option:
def price_adjust(price: int) -> int: def price_adjust(price: int) -> int:
# it is important that a base price of 0 always returns 0 as new price! # it is important that a base price of 0 always returns 0 as new price!
return int(price * (0.5 + world.random.random() * 2)) return int(price * (0.5 + world.random.random() * 2))
for item in total_inventory: def adjust_item(item):
if item: if item:
item["price"] = price_adjust(item["price"]) item["price"] = price_adjust(item["price"])
item['replacement_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 total_inventory:
for item in shop.inventory: adjust_item(item)
if item: for shop in upgrade_shops:
item['price'] = price_adjust(item["price"]) for item in shop.inventory:
item['replacement_price'] = price_adjust(item["price"]) adjust_item(item)
if 'i' in option: if 'i' in option:
world.random.shuffle(total_inventory) world.random.shuffle(total_inventory)
@ -405,7 +409,7 @@ def set_up_take_anys(world, player):
entrance = world.get_region(reg, player).entrances[0] entrance = world.get_region(reg, player).entrances[0]
connect_entrance(world, entrance.name, old_man_take_any.name, player) connect_entrance(world, entrance.name, old_man_take_any.name, player)
entrance.target = 0x58 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) world.shops.append(old_man_take_any.shop)
swords = [item for item in world.itempool if item.type == 'Sword' and item.player == player] 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] entrance = world.get_region(reg, player).entrances[0]
connect_entrance(world, entrance.name, take_any.name, player) connect_entrance(world, entrance.name, take_any.name, player)
entrance.target = target 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) world.shops.append(take_any.shop)
take_any.shop.add_inventory(0, 'Blue Potion', 0, 0) take_any.shop.add_inventory(0, 'Blue Potion', 0, 0)
take_any.shop.add_inventory(1, 'Boss Heart Container', 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 # Note: ideally exits should be copied here, but the current use case (Take anys) do not require this
if region.shop: 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) ret.shops.append(new_reg.shop)
for location in world.dynamic_locations: for location in world.dynamic_locations:

View File

@ -1,6 +1,6 @@
import collections 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): def create_regions(world, player):
@ -365,12 +365,15 @@ def mark_light_world_regions(world, player: int):
def create_shops(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(): 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': if world.mode[player] == 'inverted' and region_name == 'Dark Lake Hylia Shop':
locked = True locked = True
inventory = [('Blue Potion', 160), ('Blue Shield', 50), ('Bombs (10)', 50)] inventory = [('Blue Potion', 160), ('Blue Shield', 50), ('Bombs (10)', 50)]
region = world.get_region(region_name, player) 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 region.shop = shop
world.shops.append(shop) world.shops.append(shop)
for index, item in enumerate(inventory): for index, item in enumerate(inventory):