Simplify ShopSlot Shuffling
This commit is contained in:
		
							parent
							
								
									52d5b96435
								
							
						
					
					
						commit
						f12259dd7d
					
				|  | @ -981,7 +981,7 @@ class Dungeon(object): | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return self.world.get_name_string_for_object(self) if self.world else f'{self.name} (Player {self.player})' |         return self.world.get_name_string_for_object(self) if self.world else f'{self.name} (Player {self.player})' | ||||||
| 
 | 
 | ||||||
| class Boss(object): | class Boss(): | ||||||
|     def __init__(self, name, enemizer_name, defeat_rule, player: int): |     def __init__(self, name, enemizer_name, defeat_rule, player: int): | ||||||
|         self.name = name |         self.name = name | ||||||
|         self.enemizer_name = enemizer_name |         self.enemizer_name = enemizer_name | ||||||
|  | @ -991,7 +991,12 @@ class Boss(object): | ||||||
|     def can_defeat(self, state) -> bool: |     def can_defeat(self, state) -> bool: | ||||||
|         return self.defeat_rule(state, self.player) |         return self.defeat_rule(state, self.player) | ||||||
| 
 | 
 | ||||||
| class Location(object): | 
 | ||||||
|  | class Location(): | ||||||
|  |     shop_slot: bool = False | ||||||
|  |     event: bool = False | ||||||
|  |     locked: bool = False | ||||||
|  | 
 | ||||||
|     def __init__(self, player: int, name: str = '', address=None, crystal: bool = False, |     def __init__(self, player: int, name: str = '', address=None, crystal: bool = False, | ||||||
|                  hint_text: Optional[str] = None, parent=None, |                  hint_text: Optional[str] = None, parent=None, | ||||||
|                  player_address=None): |                  player_address=None): | ||||||
|  | @ -1004,8 +1009,6 @@ class Location(object): | ||||||
|         self.spot_type = 'Location' |         self.spot_type = 'Location' | ||||||
|         self.hint_text: str = hint_text if hint_text else name |         self.hint_text: str = hint_text if hint_text else name | ||||||
|         self.recursion_count = 0 |         self.recursion_count = 0 | ||||||
|         self.event = False |  | ||||||
|         self.locked = False |  | ||||||
|         self.always_allow = lambda item, state: False |         self.always_allow = lambda item, state: False | ||||||
|         self.access_rule = lambda state: True |         self.access_rule = lambda state: True | ||||||
|         self.item_rule = lambda item: True |         self.item_rule = lambda item: True | ||||||
|  | @ -1152,7 +1155,8 @@ class Shop(): | ||||||
|         self.inventory = [None] * self.slots |         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, | ||||||
|  |                       player: int = 0): | ||||||
|         self.inventory[slot] = { |         self.inventory[slot] = { | ||||||
|             'item': item, |             'item': item, | ||||||
|             'price': price, |             'price': price, | ||||||
|  | @ -1160,10 +1164,10 @@ class Shop(): | ||||||
|             'replacement': replacement, |             'replacement': replacement, | ||||||
|             'replacement_price': replacement_price, |             'replacement_price': replacement_price, | ||||||
|             'create_location': create_location, |             'create_location': create_location, | ||||||
|             'player': 0 |             'player': player | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     def push_inventory(self, slot: int, item: str, price: int, max: int = 1): |     def push_inventory(self, slot: int, item: str, price: int, max: int = 1, player: int = 0): | ||||||
|         if not self.inventory[slot]: |         if not self.inventory[slot]: | ||||||
|             raise ValueError("Inventory can't be pushed back if it doesn't exist") |             raise ValueError("Inventory can't be pushed back if it doesn't exist") | ||||||
| 
 | 
 | ||||||
|  | @ -1174,9 +1178,11 @@ class Shop(): | ||||||
|             'replacement': self.inventory[slot]["item"], |             'replacement': self.inventory[slot]["item"], | ||||||
|             'replacement_price': self.inventory[slot]["price"], |             'replacement_price': self.inventory[slot]["price"], | ||||||
|             'create_location': self.inventory[slot]["create_location"], |             'create_location': self.inventory[slot]["create_location"], | ||||||
|             'player': self.inventory[slot]["player"] |             'player': player | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |     def can_push_inventory(self, slot: int): | ||||||
|  |         return self.inventory[slot] and not self.inventory[slot]["replacement"] | ||||||
| 
 | 
 | ||||||
| class TakeAny(Shop): | class TakeAny(Shop): | ||||||
|     type = ShopType.TakeAny |     type = ShopType.TakeAny | ||||||
|  |  | ||||||
|  | @ -589,7 +589,8 @@ def create_dynamic_shop_locations(world, player): | ||||||
| 
 | 
 | ||||||
|                     world.clear_location_cache() |                     world.clear_location_cache() | ||||||
| 
 | 
 | ||||||
|                     world.push_item(loc, ItemFactory(item['item'], player), False)  |                     world.push_item(loc, ItemFactory(item['item'], player), False) | ||||||
|  |                     loc.shop_slot = True | ||||||
|                     loc.event = True |                     loc.event = True | ||||||
|                     loc.locked = True |                     loc.locked = True | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										80
									
								
								Main.py
								
								
								
								
							
							
						
						
									
										80
									
								
								Main.py
								
								
								
								
							|  | @ -9,7 +9,7 @@ import time | ||||||
| import zlib | import zlib | ||||||
| import concurrent.futures | import concurrent.futures | ||||||
| 
 | 
 | ||||||
| from BaseClasses import World, CollectionState, Item, Region, Location, PlandoItem | from BaseClasses import World, CollectionState, Item, Region, Location, Shop | ||||||
| from Items import ItemFactory, item_table, item_name_groups | from Items import ItemFactory, item_table, item_name_groups | ||||||
| from Regions import create_regions, create_shops, mark_light_world_regions, lookup_vanilla_location_to_entrance, SHOP_ID_START | from Regions import create_regions, create_shops, mark_light_world_regions, lookup_vanilla_location_to_entrance, SHOP_ID_START | ||||||
| from InvertedRegions import create_inverted_regions, mark_dark_world_regions | from InvertedRegions import create_inverted_regions, mark_dark_world_regions | ||||||
|  | @ -215,69 +215,47 @@ def main(args, seed=None): | ||||||
|         blacklist_word in item_name for blacklist_word in blacklist_words)} |         blacklist_word in item_name for blacklist_word in blacklist_words)} | ||||||
|     blacklist_words.add("Bee") |     blacklist_words.add("Bee") | ||||||
|     candidates = [location for location in world.get_locations() if |     candidates = [location for location in world.get_locations() if | ||||||
|                   not location.locked and not location.item.name in blacklist_words] |                   not location.locked and | ||||||
|  |                   not location.shop_slot and | ||||||
|  |                   not location.item.name in blacklist_words] | ||||||
|  | 
 | ||||||
|     world.random.shuffle(candidates) |     world.random.shuffle(candidates) | ||||||
|     shop_slots = [item for sublist in [shop.region.locations for shop in world.shops] for item in sublist if |     shop_slots = [item for sublist in (shop.region.locations for shop in world.shops) for item in sublist if | ||||||
|                   item.name != 'Potion Shop']  # TODO: "w" in shop_shuffle options? |                   item.shop_slot] | ||||||
|     shop_slots_adjusted = [] | 
 | ||||||
|     shop_items = [] |  | ||||||
|      |  | ||||||
|     for location in shop_slots: |     for location in shop_slots: | ||||||
|         slot_num = int(location.name[-1]) - 1 |         slot_num = int(location.name[-1]) - 1 | ||||||
|         shop_item = location.parent_region.shop.inventory[slot_num] |         shop: Shop = location.parent_region.shop | ||||||
|         item = location.item |         if shop.can_push_inventory(slot_num): | ||||||
|         # 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 |             for c in candidates:  # chosen item locations | ||||||
|                 if c.parent_region.shop or c is location: 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... |                 if c.item_rule(location.item):  # if rule is good... | ||||||
|                     logging.debug('Swapping {} with {}:: {} ||| {}'.format(c, location, c.item, location.item)) |                     logging.debug('Swapping {} with {}:: {} ||| {}'.format(c, location, c.item, location.item)) | ||||||
|                     c.item, location.item = location.item, c.item |                     c.item, location.item = location.item, c.item | ||||||
|                     if not world.can_beat_game():  |                     if not world.can_beat_game(): | ||||||
|                         c.item, location.item = location.item, c.item |                         c.item, location.item = location.item, c.item | ||||||
|                     else:  |                     else: | ||||||
|                         shop_slots_adjusted.append(location) |                         # we use this candidate | ||||||
|  |                         candidates.remove(c) | ||||||
|                         break |                         break | ||||||
|         # update table to location data |  | ||||||
|         item = location.item |  | ||||||
|         if (shop_item and shop_item['item'] == item.name) or 'Rupee' in item.name or item.name == '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: |             else: | ||||||
|                 shop_item['replacement'] = shop_item['item'] |                 logging.warning("Ran out of ShopShuffle Item candidate locations.") | ||||||
|                 shop_item['replacement_price'] = shop_item['price'] |                 break  # we ran out of candidates | ||||||
|             shop_item['item'] = item.name | 
 | ||||||
|             if any(x in shop_item['item'] for x in ['Single Bomb', 'Single Arrow']): |             # update table to location data | ||||||
|                 shop_item['price'] = world.random.randrange(5, 35) |             item_name = location.item.name | ||||||
|             elif any(x in shop_item['item'] for x in ['Arrows', 'Bombs', 'Clock']): |             if any(x in item_name for x in ['Single Bomb', 'Single Arrow']): | ||||||
|                 shop_item['price'] = world.random.randrange(20, 120) |                 price = world.random.randrange(1, 7) | ||||||
|             elif any(x in shop_item['item'] for x in ['Compass', 'Map', 'Small Key', 'Piece of Heart']): |             elif any(x in item_name for x in ['Arrows', 'Bombs', 'Clock']): | ||||||
|                 shop_item['price'] = world.random.randrange(50, 150) |                 price = world.random.randrange(4, 24) | ||||||
|  |             elif any(x in item_name for x in ['Compass', 'Map', 'Small Key', 'Piece of Heart']): | ||||||
|  |                 price = world.random.randrange(10, 30) | ||||||
|             else: |             else: | ||||||
|                 shop_item['price'] = world.random.randrange(50,300) |                 price = world.random.randrange(10, 60) | ||||||
| 
 | 
 | ||||||
|             shop_item['max'] = 1 |             price *= 5 | ||||||
|             shop_item['player'] = item.player if item.player != location.player else 0 |  | ||||||
|             shop_items.append(shop_item) |  | ||||||
| 
 | 
 | ||||||
|     if len(shop_items) > 0: |             shop.push_inventory(slot_num, item_name, price, 1, | ||||||
|         my_prices = [my_item['price'] for my_item in shop_items] |                                 location.item.player if location.item.player != location.player else 0) | ||||||
|         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.') |     logger.info('Patching ROM.') | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -415,9 +415,11 @@ def create_shops(world, player: int): | ||||||
|             else: |             else: | ||||||
|                 if my_shop_slots.pop(): |                 if my_shop_slots.pop(): | ||||||
|                     additional_item = world.random.choice(['Rupees (50)', 'Rupees (100)', 'Rupees (300)']) |                     additional_item = world.random.choice(['Rupees (50)', 'Rupees (100)', 'Rupees (300)']) | ||||||
|                     world.itempool.append(ItemFactory(additional_item, player)) |  | ||||||
|                     slot_name = "{} Slot {}".format(shop.region.name, index + 1) |                     slot_name = "{} Slot {}".format(shop.region.name, index + 1) | ||||||
|                     loc = Location(player, slot_name, address=shop_table_by_location[slot_name], parent=shop.region) |                     loc = Location(player, slot_name, address=shop_table_by_location[slot_name], parent=shop.region) | ||||||
|  |                     loc.shop_slot = True | ||||||
|  |                     loc.locked = True | ||||||
|  |                     loc.item = ItemFactory(additional_item, player) | ||||||
|                     shop.region.locations.append(loc) |                     shop.region.locations.append(loc) | ||||||
|                     world.dynamic_locations.append(loc) |                     world.dynamic_locations.append(loc) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								Rom.py
								
								
								
								
							
							
						
						
									
										8
									
								
								Rom.py
								
								
								
								
							|  | @ -683,17 +683,11 @@ def patch_rom(world, rom, player, team, enemized): | ||||||
|     # patch items |     # patch items | ||||||
| 
 | 
 | ||||||
|     for location in world.get_locations(): |     for location in world.get_locations(): | ||||||
|         if location.player != player: |         if location.player != player or location.address is None or location.shop_slot: | ||||||
|             continue |             continue | ||||||
| 
 | 
 | ||||||
|         itemid = location.item.code if location.item is not None else 0x5A |         itemid = location.item.code if location.item is not None else 0x5A | ||||||
| 
 | 
 | ||||||
|         if location.address is None: |  | ||||||
|             continue |  | ||||||
| 
 |  | ||||||
|         if location.parent_region.shop and 'Slot' in location.name: |  | ||||||
|             continue |  | ||||||
| 
 |  | ||||||
|         if not location.crystal: |         if not location.crystal: | ||||||
|             if location.item is not None: |             if location.item is not None: | ||||||
|                 # Keys in their native dungeon should use the orignal item code for keys |                 # Keys in their native dungeon should use the orignal item code for keys | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue