Merge remote-tracking branch 'pepper/multishop-all' into multishop

# Conflicts:
#	EntranceRandomizer.py
#	Rom.py
#	WebHostLib/static/static/playerSettings.json
#	data/basepatch.bmbp
This commit is contained in:
Fabian Dill
2021-01-09 16:03:59 +01:00
14 changed files with 508 additions and 33 deletions

69
Main.py
View File

@@ -83,6 +83,8 @@ def main(args, seed=None):
world.triforce_pieces_available = args.triforce_pieces_available.copy()
world.triforce_pieces_required = args.triforce_pieces_required.copy()
world.shop_shuffle = args.shop_shuffle.copy()
world.shop_shuffle_slots = args.shop_shuffle_slots.copy()
world.potion_shop_shuffle = args.potion_shop_shuffle.copy()
world.progression_balancing = {player: not balance for player, balance in args.skip_progression_balancing.items()}
world.shuffle_prizes = args.shuffle_prizes.copy()
world.sprite_pool = args.sprite_pool.copy()
@@ -205,10 +207,74 @@ def main(args, seed=None):
distribute_items_restrictive(world, True)
elif args.algorithm == 'balanced':
distribute_items_restrictive(world, True)
if world.players > 1:
balance_multiworld_progression(world)
candidates = [l for l in world.get_locations() if l.item.name in ['Bee Trap', 'Shovel', 'Bug Catching Net', 'Cane of Byrna', 'Triforce Piece'] or
any([x in l.item.name for x in ['Key', 'Map', 'Compass', 'Clock', 'Heart', 'Sword', 'Shield', 'Bomb', 'Arrow', 'Mail']])]
world.random.shuffle(candidates)
shop_slots = [item for sublist in [shop.region.locations for shop in world.shops] for item in sublist if item.name != 'Potion Shop']
shop_slots_adjusted = []
shop_items = []
for location in shop_slots:
slot_num = int(location.name[-1]) - 1
shop_item = location.parent_region.shop.inventory[slot_num]
item = location.item
# if item is a rupee or single bee, or identical, swap it out
if (shop_item is not None and shop_item['item'] == item.name) or 'Rupee' in item.name or (item.name in ['Bee']):
for c in candidates: # chosen item locations
if 'Rupee' in c.item.name or c.item.name in 'Bee': continue
if (shop_item is not None and shop_item['item'] == c.item.name): continue
if c.item_rule(location.item): # if rule is good...
logging.debug('Swapping {} with {}:: {} ||| {}'.format(c, location, c.item, location.item))
c.item, location.item = location.item, c.item
if not world.can_beat_game():
c.item, location.item = location.item, c.item
else:
shop_slots_adjusted.append(location)
break
# update table to location data
item = location.item
if (shop_item is not None and shop_item['item'] == item.name) or 'Rupee' in item.name or (item.name in ['Bee']):
# this will filter items that match the item in the shop or Rupees, or single bees
# really not a way for the player to know a renewable item from a player pool item
# bombs can be sitting on top of arrows or a potion refill, but dunno if that's a big deal
# this should rarely happen with the above code in place, and could be an option in config if necessary
logging.debug('skipping item shop {}'.format(item.name))
else:
if shop_item is None:
location.parent_region.shop.add_inventory(slot_num, 'None', 0)
shop_item = location.parent_region.shop.inventory[slot_num]
else:
shop_item['replacement'] = shop_item['item']
shop_item['replacement_price'] = shop_item['price']
shop_item['item'] = item.name
if any([x in shop_item['item'] for x in ['Single Bomb', 'Single Arrow']]):
shop_item['price'] = world.random.randrange(5,35)
elif any([x in shop_item['item'] for x in ['Arrows', 'Bombs', 'Clock']]):
shop_item['price'] = world.random.randrange(20,120)
elif any([x in shop_item['item'] for x in ['Universal Key', 'Compass', 'Map', 'Small Key', 'Piece of Heart']]):
shop_item['price'] = world.random.randrange(50,150)
else:
shop_item['price'] = world.random.randrange(50,300)
shop_item['max'] = 1
shop_item['player'] = item.player if item.player != location.player else 0
shop_items.append(shop_item)
if len(shop_items) > 0:
my_prices = [my_item['price'] for my_item in shop_items]
price_scale = (80*max(8, len(my_prices)+2))/sum(my_prices)
for i in shop_items:
i['price'] *= price_scale
if i['price'] < 5: i['price'] = 5
else: i['price'] = int((i['price']//5)*5)
logging.debug('Adjusting {} of {} shop slots'.format(len(shop_slots_adjusted), len(shop_slots)))
logging.debug('Adjusted {} into shops'.format([x.item.name for x in shop_slots_adjusted]))
logger.info('Patching ROM.')
outfilebase = 'BM_%s' % (args.outputname if args.outputname else world.seed)
@@ -336,6 +402,7 @@ def main(args, seed=None):
main_entrance = get_entrance_to_region(region)
for location in region.locations:
if type(location.address) == int: # skips events and crystals
if location.address >= 0x400000: continue
if lookup_vanilla_location_to_entrance[location.address] != main_entrance.name:
er_hint_data[region.player][location.address] = main_entrance.name