 2f7e532f4f
			
		
	
	2f7e532f4f
	
	
	
		
			
			Squash merge, original Commits: * Minecraft locations, items, and generation without logic * added id lookup for minecraft * typing import fix in minecraft/Items.py * fix 2 * implementing Minecraft options and hard/postgame advancement exclusion * first logic pass (75/80) * logic pass 2 and proper completion conditions * added insane difficulty pool, modified method of excluding item pools for easier extension * bump network_data_package version * minecraft testing framework * switch Ancient Debris to Netherite Scrap to avoid advancement triggering on receiving that item * Testing now functions, split tests up by advancement pane, added some story tests * Newer testing framework: every advancement gets its own function, for ease of testing * fixed logic for The End... Again... * changed option names to "include_hard_advancements" etc. * village/pillager-related advancements now require can_adventure: weapon + food * a few minecraft tests * rename "Flint & Steel" to "Flint and Steel" for parity with in-game name * additional MC tests * more tests, mostly nether-related tests * more tests, removed anvil path for Two Birds One Arrow * include Minecraft slot data, and a world seed for each Minecraft player slot * Added new items: ender pearls, lapis, porkchops * All remaining Minecraft tests * formatting of Minecraft tests and logic for better readability * require Wither kill for Monsters Hunted * properly removed 8 Emeralds item from item pool * enchanting required for wither; fishing rod required for water breathing; water breathing required for elder guardian kill * Added 12 new advancements (ported from old achievement system) * renamed "On a Rail" for consistency with modern advancements * tests for the new advancements * moved slot_data generation for minecraft into worlds/minecraft/__init__.py, added logic_version to slot_data * output minecraft options in the spoiler log * modified advancement goal values for new advancements * make non-native Minecraft items appear as Shovel in ALttP, and unknown-game items as Power Stars * fixed glowstone block logic for Not Quite Nine Lives * setup for shuffling MC structures: building ER world and shuffling regions/entrances * ensured Nether Fortresses can't be placed in the End * finished logic for structure randomization * fixed nonnative items always showing up as Hammers in ALttP shops * output minecraft structure info in the spoiler * generate .apmc file for communication with MC client * fixed structure rando always using the same seed * move stuff to worlds/minecraft/Regions.py * make output apmc file have consistent name with other files * added minecraft bottle macro; fixed tests imports * generalizing MC region generation * restructured structure shuffling in preparation for structure plando * only output structure rando info in spoiler if they are shuffled * Force structure rando to always be off, for the stable release * added Minecraft options to player settings * formally added combat_difficulty as an option * Added Ender Dragon into playthrough, cleaned up goal map * Added new difficulties: Easy, Normal, Hard combat * moved .apmc generation time to prevent outputs on failed generation * updated tests for new combat logic * Fixed bug causing generation to fail; removed Nether Fortress event since it should no longer be needed with the fix * moved all MC-specific functions into gen_minecraft * renamed "logic_version" to "client_version" * bug fixes properly flagged event locations/items with id None moved generation back to Main.py to fix mysterious generation failures * moved link_minecraft_regions into minecraft init, left create_regions in Main for caching * added seed_name, player_name, client_version to apmc file * reenabled structure shuffle * added entrance tests for minecraft Co-authored-by: achuang <alexander.w.chuang@gmail.com>
		
			
				
	
	
		
			102 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			102 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from .Locations import MinecraftAdvancement, advancement_table
 | |
| 
 | |
| from BaseClasses import Region, Entrance, Location, MultiWorld, Item
 | |
| 
 | |
| def minecraft_create_regions(world: MultiWorld, player: int):
 | |
| 
 | |
|     def MCRegion(region_name: str, exits=[]):
 | |
|         ret = Region(region_name, None, region_name, player)
 | |
|         ret.world = world
 | |
|         ret.locations = [ MinecraftAdvancement(player, loc_name, loc_data.id, ret) 
 | |
|             for loc_name, loc_data in advancement_table.items() 
 | |
|             if loc_data.region == region_name ]
 | |
|         for exit in exits: 
 | |
|             ret.exits.append(Entrance(player, exit, ret))
 | |
|         return ret
 | |
| 
 | |
|     world.regions += [MCRegion(*r) for r in mc_regions]
 | |
| 
 | |
|     for (exit, region) in mandatory_connections:
 | |
|         world.get_entrance(exit, player).connect(world.get_region(region, player))
 | |
| 
 | |
| def link_minecraft_structures(world: MultiWorld, player: int):
 | |
| 
 | |
|     # Get all unpaired exits and all regions without entrances (except the Menu)
 | |
|     # This function is destructive on these lists. 
 | |
|     exits = [exit.name for r in world.regions if r.player == player for exit in r.exits if exit.connected_region == None]
 | |
|     structs = [r.name for r in world.regions if r.player == player and r.entrances == [] and r.name != 'Menu']
 | |
|     try: 
 | |
|         assert len(exits) == len(structs)
 | |
|     except AssertionError as e: # this should never happen
 | |
|         raise Exception(f"Could not obtain equal numbers of Minecraft exits and structures for player {player}") from e
 | |
|     num_regions = len(exits)
 | |
|     pairs = {}
 | |
| 
 | |
|     def check_valid_connection(exit, struct): 
 | |
|         if (exit in exits) and (struct in structs) and (exit not in pairs): 
 | |
|             return True
 | |
|         return False
 | |
| 
 | |
|     def set_pair(exit, struct): 
 | |
|         pairs[exit] = struct
 | |
|         exits.remove(exit)
 | |
|         structs.remove(struct)
 | |
| 
 | |
|     # Plando stuff. Remove any utilized exits/structs from the lists. 
 | |
|     # Raise error if trying to put Nether Fortress in the End. 
 | |
| 
 | |
|     if world.shuffle_structures[player]: 
 | |
|         # Can't put Nether Fortress in the End
 | |
|         if 'The End Structure' in exits and 'Nether Fortress' in structs: 
 | |
|             try: 
 | |
|                 end_struct = world.random.choice([s for s in structs if s != 'Nether Fortress'])
 | |
|                 set_pair('The End Structure', end_struct)
 | |
|             except IndexError as e: 
 | |
|                 raise Exception(f"Plando forced Nether Fortress in the End for player {player}") from e
 | |
|         world.random.shuffle(structs)
 | |
|         for exit, struct in zip(exits[:], structs[:]): 
 | |
|             set_pair(exit, struct)
 | |
|     else: # write remaining default connections
 | |
|         for (exit, struct) in default_connections: 
 | |
|             if exit in exits: 
 | |
|                 set_pair(exit, struct)
 | |
| 
 | |
|     # Make sure we actually paired everything; might fail if plando
 | |
|     try:
 | |
|         assert len(exits) == len(structs) == 0
 | |
|     except AssertionError as e: 
 | |
|         raise Exception(f"Failed to connect all Minecraft structures for player {player}; check plando settings in yaml") from e
 | |
| 
 | |
|     for exit, struct in pairs.items():
 | |
|         world.get_entrance(exit, player).connect(world.get_region(struct, player))
 | |
|         if world.shuffle_structures[player]:
 | |
|             world.spoiler.set_entrance(exit, struct, 'entrance', player)
 | |
| 
 | |
| # (Region name, list of exits)
 | |
| mc_regions = [
 | |
|     ('Menu', ['New World']),
 | |
|     ('Overworld', ['Nether Portal', 'End Portal', 'Overworld Structure 1', 'Overworld Structure 2']),
 | |
|     ('The Nether', ['Nether Structure 1', 'Nether Structure 2']),
 | |
|     ('The End', ['The End Structure']),
 | |
|     ('Village', []),
 | |
|     ('Pillager Outpost', []),
 | |
|     ('Nether Fortress', []),
 | |
|     ('Bastion Remnant', []),
 | |
|     ('End City', [])
 | |
| ]
 | |
| 
 | |
| # (Entrance, region pointed to)
 | |
| mandatory_connections = [
 | |
|     ('New World', 'Overworld'),
 | |
|     ('Nether Portal', 'The Nether'),
 | |
|     ('End Portal', 'The End')
 | |
| ]
 | |
| 
 | |
| default_connections = {
 | |
|     ('Overworld Structure 1', 'Village'),
 | |
|     ('Overworld Structure 2', 'Pillager Outpost'),
 | |
|     ('Nether Structure 1', 'Nether Fortress'),
 | |
|     ('Nether Structure 2', 'Bastion Remnant'),
 | |
|     ('The End Structure', 'End City')
 | |
| }
 |