mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00

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')
|
|
}
|