This commit is contained in:
espeon65536
2021-06-25 19:44:15 -05:00
65 changed files with 1075 additions and 516 deletions
+1
View File
@@ -27,6 +27,7 @@ class World(metaclass=AutoWorldRegister):
world: MultiWorld
player: int
options: dict = {}
def __init__(self, world: MultiWorld, player: int):
self.world = world
+2 -2
View File
@@ -121,8 +121,8 @@ def GanonDefeatRule(state, player: int):
can_hurt = state.has_beam_sword(player)
common = can_hurt and state.has_fire_source(player)
# silverless ganon may be needed in minor glitches
if state.world.logic[player] in {"owglitches", "minorglitches", "none"}:
# silverless ganon may be needed in anything higher than no glitches
if state.world.logic[player] != 'noglitches':
# need to light torch a sufficient amount of times
return common and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (
state.has('Silver Bow', player) and state.can_shoot_arrows(player)) or
+2 -1
View File
@@ -21,13 +21,14 @@ def parse_arguments(argv, no_defaults=False):
parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('--create_spoiler', help='Output a Spoiler File', action='store_true')
parser.add_argument('--logic', default=defval('noglitches'), const='noglitches', nargs='?', choices=['noglitches', 'minorglitches', 'owglitches', 'nologic'],
parser.add_argument('--logic', default=defval('noglitches'), const='noglitches', nargs='?', choices=['noglitches', 'minorglitches', 'owglitches', 'hybridglitches', 'nologic'],
help='''\
Select Enforcement of Item Requirements. (default: %(default)s)
No Glitches:
Minor Glitches: May require Fake Flippers, Bunny Revival
and Dark Room Navigation.
Overworld Glitches: May require overworld glitches.
Hybrid Major Glitches: May require both overworld and underworld clipping.
No Logic: Distribute items without regard for
item requirements.
''')
+10 -2
View File
@@ -1,6 +1,6 @@
# ToDo: With shuffle_ganon option, prevent gtower from linking to an exit only location through a 2 entrance cave.
from collections import defaultdict
from worlds.alttp.UnderworldGlitchRules import underworld_glitch_connections
def link_entrances(world, player):
connect_two_way(world, 'Links House', 'Links House Exit', player) # unshuffled. For now
@@ -1066,6 +1066,10 @@ def link_entrances(world, player):
raise NotImplementedError(
f'{world.shuffle[player]} Shuffling not supported yet. Player {world.get_player_names(player)}')
# mandatory hybrid major glitches connections
if world.logic[player] in ['hybridglitches', 'nologic']:
underworld_glitch_connections(world, player)
# check for swamp palace fix
if world.get_entrance('Dam', player).connected_region.name != 'Dam' or world.get_entrance('Swamp Palace', player).connected_region.name != 'Swamp Palace (Entrance)':
world.swamp_patch_required[player] = True
@@ -1767,6 +1771,10 @@ def link_inverted_entrances(world, player):
else:
raise NotImplementedError('Shuffling not supported yet')
# mandatory hybrid major glitches connections
if world.logic[player] in ['hybridglitches', 'nologic']:
underworld_glitch_connections(world, player)
# patch swamp drain
if world.get_entrance('Dam', player).connected_region.name != 'Dam' or world.get_entrance('Swamp Palace', player).connected_region.name != 'Swamp Palace (Entrance)':
world.swamp_patch_required[player] = True
@@ -1941,7 +1949,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player):
invalid_connections = Must_Exit_Invalid_Connections.copy()
invalid_cave_connections = defaultdict(set)
if world.logic[player] in ['owglitches', 'nologic']:
if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']:
from worlds.alttp import OverworldGlitchRules
for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'):
invalid_connections[entrance] = set()
+1 -1
View File
@@ -568,7 +568,7 @@ def get_pool_core(world, player: int):
return world.random.choice([True, False]) if progressive == 'random' else progressive == 'on'
# provide boots to major glitch dependent seeds
if logic in {'owglitches', 'nologic'} and world.glitch_boots[player] and goal != 'icerodhunt':
if logic in {'owglitches', 'hybridglitches', 'nologic'} and world.glitch_boots[player] and goal != 'icerodhunt':
precollected_items.append('Pegasus Boots')
pool.remove('Pegasus Boots')
pool.append('Rupees (20)')
+6 -21
View File
@@ -751,13 +751,11 @@ bonk_addresses = [0x4CF6C, 0x4CFBA, 0x4CFE0, 0x4CFFB, 0x4D018, 0x4D01B, 0x4D028,
0x4D3F8, 0x4D416, 0x4D420, 0x4D423, 0x4D42D, 0x4D449, 0x4D48C, 0x4D4D9, 0x4D4DC, 0x4D4E3,
0x4D504, 0x4D507, 0x4D55E, 0x4D56A]
def get_nonnative_item_sprite(game):
game_to_id = {
"Factorio": 0x09, # Hammer
"Hollow Knight": 0x21, # Bug Catching Net
"Minecraft": 0x13, # Shovel
}
return game_to_id.get(game, 0x6B) # default to Power Star
def get_nonnative_item_sprite(item: str) -> int:
return 0x6B # set all non-native sprites to Power Star as per 13 to 2 vote at
# https://discord.com/channels/731205301247803413/827141303330406408/852102450822905886
def patch_rom(world, rom, player, team, enemized):
local_random = world.slot_seeds[player]
@@ -1719,20 +1717,7 @@ def write_custom_shops(rom, world, player):
if item is None:
break
if not item['item'] in item_table: # item not native to ALTTP
# This is a terrible way to do this, please fix later
from worlds.hk.Items import lookup_id_to_name as hk_lookup
from worlds.factorio.Technologies import lookup_id_to_name as factorio_lookup
from worlds.minecraft.Items import lookup_id_to_name as mc_lookup
item_name = item['item']
if item_name in hk_lookup.values():
item_game = 'Hollow Knight'
elif item_name in factorio_lookup.values():
item_game = 'Factorio'
elif item_name in mc_lookup.values():
item_game = 'Minecraft'
else:
item_game = 'Generic'
item_code = get_nonnative_item_sprite(item_game)
item_code = get_nonnative_item_sprite(item['item'])
else:
item_code = ItemFactory(item['item'], player).code
if item['item'] == 'Single Arrow' and item['player'] == 0 and world.retro[player]:
+16 -9
View File
@@ -4,6 +4,7 @@ from worlds.alttp import OverworldGlitchRules
from BaseClasses import RegionType, MultiWorld, Entrance
from worlds.alttp.Items import ItemFactory, progression_items, item_name_groups
from worlds.alttp.OverworldGlitchRules import overworld_glitches_rules, no_logic_rules
from worlds.alttp.UnderworldGlitchRules import underworld_glitches_rules
from worlds.alttp.Bosses import GanonDefeatRule
from worlds.generic.Rules import set_rule, add_rule, forbid_item, add_item_rule, item_in_locations, \
item_name
@@ -47,12 +48,17 @@ def set_rules(world, player):
if world.logic[player] == 'noglitches':
no_glitches_rules(world, player)
elif world.logic[player] in ['owglitches', 'nologic']:
elif world.logic[player] == 'owglitches':
# Initially setting no_glitches_rules to set the baseline rules for some
# entrances. The overworld_glitches_rules set is primarily additive.
no_glitches_rules(world, player)
fake_flipper_rules(world, player)
overworld_glitches_rules(world, player)
elif world.logic[player] in ['hybridglitches', 'nologic']:
no_glitches_rules(world, player)
fake_flipper_rules(world, player)
overworld_glitches_rules(world, player)
underworld_glitches_rules(world, player)
elif world.logic[player] == 'minorglitches':
no_glitches_rules(world, player)
fake_flipper_rules(world, player)
@@ -68,25 +74,26 @@ def set_rules(world, player):
if world.mode[player] != 'inverted':
set_big_bomb_rules(world, player)
if world.logic[player] in {'owglitches', 'nologic'} and world.shuffle[player] not in {'insanity', 'insanity_legacy', 'madness'}:
if world.logic[player] in {'owglitches', 'hybridglitches', 'nologic'} and world.shuffle[player] not in {'insanity', 'insanity_legacy', 'madness'}:
path_to_courtyard = mirrorless_path_to_castle_courtyard(world, player)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.world.get_entrance('Dark Death Mountain Offset Mirror', player).can_reach(state) and all(rule(state) for rule in path_to_courtyard), 'or')
else:
set_inverted_big_bomb_rules(world, player)
# if swamp and dam have not been moved we require mirror for swamp palace
if not world.swamp_patch_required[player]:
# however there is mirrorless swamp in hybrid MG, so we don't necessarily want this. HMG handles this requirement itself.
if not world.swamp_patch_required[player] and world.logic[player] not in ['hybridglitches', 'nologic']:
add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player))
# GT Entrance may be required for Turtle Rock for OWG and < 7 required
ganons_tower = world.get_entrance('Inverted Ganons Tower' if world.mode[player] == 'inverted' else 'Ganons Tower', player)
if world.crystals_needed_for_gt[player] == 7 and not (world.logic[player] in ['owglitches', 'nologic'] and world.mode[player] != 'inverted'):
if world.crystals_needed_for_gt[player] == 7 and not (world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and world.mode[player] != 'inverted'):
set_rule(ganons_tower, lambda state: False)
set_trock_key_rules(world, player)
set_rule(ganons_tower, lambda state: state.has_crystals(state.world.crystals_needed_for_gt[player], player))
if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'nologic']:
if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']:
add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.world.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or')
set_bunny_rules(world, player, world.mode[player] == 'inverted')
@@ -1387,7 +1394,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
def get_rule_to_add(region, location = None, connecting_entrance = None):
# In OWG, a location can potentially be superbunny-mirror accessible or
# bunny revival accessible.
if world.logic[player] in ['minorglitches', 'owglitches', 'nologic']:
if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic']:
if region.name == 'Swamp Palace (Entrance)': # Need to 0hp revive - not in logic
return lambda state: state.has('Moon Pearl', player)
if region.name == 'Tower of Hera (Bottom)': # Need to hit the crystal switch
@@ -1427,7 +1434,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
seen.add(new_region)
if not is_link(new_region):
# For glitch rulesets, establish superbunny and revival rules.
if world.logic[player] in ['minorglitches', 'owglitches', 'nologic'] and entrance.name not in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] and entrance.name not in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions():
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has('Magic Mirror', player) and state.has_sword(player))
elif (region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions()
@@ -1465,7 +1472,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
# Add requirements for all locations that are actually in the dark world, except those available to the bunny, including dungeon revival
for entrance in world.get_entrances():
if entrance.player == player and is_bunny(entrance.connected_region):
if world.logic[player] in ['minorglitches', 'owglitches', 'nologic'] :
if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] :
if entrance.connected_region.type == RegionType.Dungeon:
if entrance.parent_region.type != RegionType.Dungeon and entrance.connected_region.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
add_rule(entrance, get_rule_to_add(entrance.connected_region, None, entrance))
@@ -1473,7 +1480,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
if entrance.connected_region.name == 'Turtle Rock (Entrance)':
add_rule(world.get_entrance('Turtle Rock Entrance Gap', player), get_rule_to_add(entrance.connected_region, None, entrance))
for location in entrance.connected_region.locations:
if world.logic[player] in ['minorglitches', 'owglitches', 'nologic'] and entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances():
if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] and entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances():
continue
if location.name in bunny_accessible_locations:
continue
+113
View File
@@ -0,0 +1,113 @@
from BaseClasses import Entrance
from worlds.generic.Rules import set_rule, add_rule
# We actually need the logic to properly "mark" these regions as Light or Dark world.
# Therefore we need to make these connections during the normal link_entrances stage, rather than during set_rules.
def underworld_glitch_connections(world, player):
specrock = world.get_region('Spectacle Rock Cave (Bottom)', player)
mire = world.get_region('Misery Mire (West)', player)
kikiskip = Entrance(player, 'Kiki Skip', specrock)
mire_to_hera = Entrance(player, 'Mire to Hera Clip', mire)
mire_to_swamp = Entrance(player, 'Hera to Swamp Clip', mire)
specrock.exits.append(kikiskip)
mire.exits.extend([mire_to_hera, mire_to_swamp])
if world.fix_fake_world[player]:
kikiskip.connect(world.get_entrance('Palace of Darkness Exit', player).connected_region)
mire_to_hera.connect(world.get_entrance('Tower of Hera Exit', player).connected_region)
mire_to_swamp.connect(world.get_entrance('Swamp Palace Exit', player).connected_region)
else:
kikiskip.connect(world.get_region('Palace of Darkness (Entrance)', player))
mire_to_hera.connect(world.get_region('Tower of Hera (Bottom)', player))
mire_to_swamp.connect(world.get_region('Swamp Palace (Entrance)', player))
# For some entrances, we need to fake having pearl, because we're in fake DW/LW.
# This creates a copy of the input state that has Moon Pearl.
def fake_pearl_state(state, player):
if state.has('Moon Pearl', player):
return state
fake_state = state.copy()
fake_state.prog_items['Moon Pearl', player] += 1
return fake_state
# Sets the rules on where we can actually go using this clip.
# Behavior differs based on what type of ER shuffle we're playing.
def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, dungeon_exit: str):
fix_dungeon_exits = world.fix_palaceofdarkness_exit[player]
fix_fake_worlds = world.fix_fake_world[player]
dungeon_entrance = [r for r in world.get_region(dungeon_region, player).entrances if r.name != clip.name][0]
if not fix_dungeon_exits: # vanilla, simple, restricted, dungeonssimple; should never have fake worlds fix
# Dungeons are only shuffled among themselves. We need to check SW, MM, and AT because they can't be reentered trivially.
if dungeon_entrance.name == 'Skull Woods Final Section':
set_rule(clip, lambda state: False) # entrance doesn't exist until you fire rod it from the other side
elif dungeon_entrance.name == 'Misery Mire':
add_rule(clip, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) # open the dungeon
elif dungeon_entrance.name == 'Agahnims Tower':
add_rule(clip, lambda state: state.has('Cape', player) or state.has_beam_sword(player) or state.has('Beat Agahnim 1', player)) # kill/bypass barrier
# Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally.
add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
elif not fix_fake_worlds: # full, dungeonsfull; fixed dungeon exits, but no fake worlds fix
# Entry requires the entrance's requirements plus a fake pearl, but you don't gain logical access to the surrounding region.
add_rule(clip, lambda state: dungeon_entrance.access_rule(fake_pearl_state(state, player)))
# exiting restriction
add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
# Otherwise, the shuffle type is crossed, dungeonscrossed, or insanity; all of these do not need additional rules on where we can go,
# since the clip links directly to the exterior region.
def underworld_glitches_rules(world, player):
fix_dungeon_exits = world.fix_palaceofdarkness_exit[player]
fix_fake_worlds = world.fix_fake_world[player]
# Ice Palace Entrance Clip
# This is the easiest one since it's a simple internal clip. Just need to also add melting to freezor chest since it's otherwise assumed.
add_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.can_bomb_clip(world.get_region('Ice Palace (Entrance)', player), player), combine='or')
add_rule(world.get_location('Ice Palace - Freezor Chest', player), lambda state: state.can_melt_things(player))
# Kiki Skip
kikiskip = world.get_entrance('Kiki Skip', player)
set_rule(kikiskip, lambda state: state.can_bomb_clip(kikiskip.parent_region, player))
dungeon_reentry_rules(world, player, kikiskip, 'Palace of Darkness (Entrance)', 'Palace of Darkness Exit')
# Mire -> Hera -> Swamp
# Using mire keys on other dungeon doors
mire = world.get_region('Misery Mire (West)', player)
mire_clip = lambda state: state.can_reach('Misery Mire (West)', 'Region', player) and state.can_bomb_clip(mire, player) and state.has_fire_source(player)
hera_clip = lambda state: state.can_reach('Tower of Hera (Top)', 'Region', player) and state.can_bomb_clip(world.get_region('Tower of Hera (Top)', player), player)
add_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: mire_clip(state) and state.has('Big Key (Misery Mire)', player), combine='or')
add_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: mire_clip(state), combine='or')
add_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: mire_clip(state) or hera_clip(state), combine='or')
# Build the rule for SP moat.
# We need to be able to s+q to old man, then go to either Mire or Hera at either Hera or GT.
# First we require a certain type of entrance shuffle, then build the rule from its pieces.
if not world.swamp_patch_required[player]:
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']:
rule_map = {
'Misery Mire (Entrance)': (lambda state: True),
'Tower of Hera (Bottom)': (lambda state: state.can_reach('Tower of Hera Big Key Door', 'Entrance', player))
}
inverted = world.mode[player] == 'inverted'
hera_rule = lambda state: (state.has('Moon Pearl', player) or not inverted) and \
rule_map.get(world.get_entrance('Tower of Hera', player).connected_region.name, lambda state: False)(state)
gt_rule = lambda state: (state.has('Moon Pearl', player) or inverted) and \
rule_map.get(world.get_entrance(('Ganons Tower' if not inverted else 'Inverted Ganons Tower'), player).connected_region.name, lambda state: False)(state)
mirrorless_moat_rule = lambda state: state.can_reach('Old Man S&Q', 'Entrance', player) and mire_clip(state) and (hera_rule(state) or gt_rule(state))
add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player) or mirrorless_moat_rule(state))
else:
add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player))
# Using the entrances for various ER types. Hera -> Swamp never matters because you can only logically traverse with the mire keys
mire_to_hera = world.get_entrance('Mire to Hera Clip', player)
mire_to_swamp = world.get_entrance('Hera to Swamp Clip', player)
set_rule(mire_to_hera, mire_clip)
set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has('Flippers', player))
dungeon_reentry_rules(world, player, mire_to_hera, 'Tower of Hera (Bottom)', 'Tower of Hera Exit')
dungeon_reentry_rules(world, player, mire_to_swamp, 'Swamp Palace (Entrance)', 'Swamp Palace Exit')
+5 -4
View File
@@ -9,9 +9,9 @@ import json
import jinja2
import Utils
import shutil
import Options
from . import Options
from BaseClasses import MultiWorld
from .Technologies import tech_table, rocket_recipes, recipes
from .Technologies import tech_table, rocket_recipes, recipes, free_sample_blacklist
template_env: Optional[jinja2.Environment] = None
@@ -75,8 +75,9 @@ def generate_mod(world: MultiWorld, player: int):
"rocket_recipe": rocket_recipes[world.max_science_pack[player].value],
"slot_name": world.player_names[player][0], "seed_name": world.seed_name,
"starting_items": world.starting_items[player], "recipes": recipes,
"random": world.slot_seeds[player],
"recipe_time_scale": recipe_time_scales[world.recipe_time[player].value]}
"random": world.slot_seeds[player], "static_nodes": world.worlds[player].static_nodes,
"recipe_time_scale": recipe_time_scales[world.recipe_time[player].value],
"free_sample_blacklist": {item : 1 for item in free_sample_blacklist}}
for factorio_option in Options.factorio_options:
template_data[factorio_option] = getattr(world, factorio_option)[player].value
+85
View File
@@ -0,0 +1,85 @@
import typing
from Options import Choice, OptionDict, Option, DefaultOnToggle
class MaxSciencePack(Choice):
option_automation_science_pack = 0
option_logistic_science_pack = 1
option_military_science_pack = 2
option_chemical_science_pack = 3
option_production_science_pack = 4
option_utility_science_pack = 5
option_space_science_pack = 6
default = 6
def get_allowed_packs(self):
return {option.replace("_", "-") for option, value in self.options.items() if value <= self.value} - \
{"space-science-pack"} # with rocket launch being the goal, post-launch techs don't make sense
class TechCost(Choice):
option_very_easy = 0
option_easy = 1
option_kind = 2
option_normal = 3
option_hard = 4
option_very_hard = 5
option_insane = 6
default = 3
class FreeSamples(Choice):
option_none = 0
option_single_craft = 1
option_half_stack = 2
option_stack = 3
default = 3
class TechTreeLayout(Choice):
option_single = 0
option_small_diamonds = 1
option_medium_diamonds = 2
option_large_diamonds = 3
option_small_pyramids = 4
option_medium_pyramids = 5
option_large_pyramids = 6
option_small_funnels = 7
option_medium_funnels = 8
option_large_funnels = 9
option_funnels = 4
alias_pyramid = 6
alias_funnel = 9
default = 0
class TechTreeInformation(Choice):
option_none = 0
option_advancement = 1
option_full = 2
default = 2
class RecipeTime(Choice):
option_vanilla = 0
option_fast = 1
option_normal = 2
option_slow = 4
option_chaos = 5
class FactorioStartItems(OptionDict):
default = {"burner-mining-drill": 19, "stone-furnace": 19}
factorio_options: typing.Dict[str, type(Option)] = {
"max_science_pack": MaxSciencePack,
"tech_tree_layout": TechTreeLayout,
"tech_cost": TechCost,
"free_samples": FreeSamples,
"tech_tree_information": TechTreeInformation,
"starting_items": FactorioStartItems,
"recipe_time": RecipeTime,
"imported_blueprints": DefaultOnToggle,
}
+1 -2
View File
@@ -1,7 +1,6 @@
from typing import Dict, List, Set
from BaseClasses import MultiWorld
from Options import TechTreeLayout
from worlds.factorio.Options import TechTreeLayout
funnel_layers = {TechTreeLayout.option_small_funnels: 3,
TechTreeLayout.option_medium_funnels: 4,
+16 -9
View File
@@ -4,11 +4,12 @@ from typing import Dict, Set, FrozenSet
import os
import json
import Options
import Utils
import logging
import functools
from . import Options
factorio_id = 2 ** 17
source_folder = Utils.local_path("data", "factorio")
@@ -66,7 +67,7 @@ class CustomTechnology(Technology):
def __init__(self, origin: Technology, world, allowed_packs: Set[str], player: int):
ingredients = origin.ingredients & allowed_packs
self.player = player
if world.random_tech_ingredients[player] and origin.name not in world.worlds[player].static_nodes:
if origin.name not in world.worlds[player].static_nodes:
ingredients = list(ingredients)
ingredients.sort() # deterministic sample
ingredients = world.random.sample(ingredients, world.random.randint(1, len(ingredients)))
@@ -99,6 +100,7 @@ class Machine(FactorioElement):
self.name: str = name
self.categories: set = categories
# recipes and technologies can share names in Factorio
for technology_name in sorted(raw):
data = raw[technology_name]
@@ -125,7 +127,8 @@ for recipe_name, recipe_data in raw_recipes.items():
recipe = Recipe(recipe_name, recipe_data["category"], set(recipe_data["ingredients"]), set(recipe_data["products"]))
recipes[recipe_name] = Recipe
if recipe.products.isdisjoint(recipe.ingredients) and "empty-barrel" not in recipe.products: # prevents loop recipes like uranium centrifuging
if recipe.products.isdisjoint(
recipe.ingredients) and "empty-barrel" not in recipe.products: # prevents loop recipes like uranium centrifuging
for product_name in recipe.products:
all_product_sources.setdefault(product_name, set()).add(recipe)
@@ -153,6 +156,7 @@ def unlock_just_tech(recipe: Recipe, _done) -> Set[Technology]:
current_technologies |= recursively_get_unlocking_technologies(ingredient_name, _done)
return current_technologies
def unlock(recipe: Recipe, _done) -> Set[Technology]:
current_technologies = set()
current_technologies |= recipe.unlocking_technologies
@@ -162,7 +166,9 @@ def unlock(recipe: Recipe, _done) -> Set[Technology]:
return current_technologies
def recursively_get_unlocking_technologies(ingredient_name, _done=None, unlock_func=unlock_just_tech) -> Set[Technology]:
def recursively_get_unlocking_technologies(ingredient_name, _done=None, unlock_func=unlock_just_tech) -> Set[
Technology]:
if _done:
if ingredient_name in _done:
return set()
@@ -180,7 +186,6 @@ def recursively_get_unlocking_technologies(ingredient_name, _done=None, unlock_f
return current_technologies
required_machine_technologies: Dict[str, FrozenSet[Technology]] = {}
for ingredient_name in machines:
required_machine_technologies[ingredient_name] = frozenset(recursively_get_unlocking_technologies(ingredient_name))
@@ -192,14 +197,14 @@ for machine in machines.values():
if machine != pot_source_machine \
and machine.categories.issuperset(pot_source_machine.categories) \
and required_machine_technologies[machine.name].issuperset(
required_machine_technologies[pot_source_machine.name]):
required_machine_technologies[pot_source_machine.name]):
logically_useful = False
break
if logically_useful:
logical_machines[machine.name] = machine
del(required_machine_technologies)
del (required_machine_technologies)
machines_per_category: Dict[str: Set[Machine]] = {}
for machine in logical_machines.values():
@@ -219,11 +224,11 @@ for ingredient_name in all_ingredient_names:
required_technologies[ingredient_name] = frozenset(
recursively_get_unlocking_technologies(ingredient_name, unlock_func=unlock))
advancement_technologies: Set[str] = set()
for technologies in required_technologies.values():
advancement_technologies |= {technology.name for technology in technologies}
@functools.lru_cache(10)
def get_rocket_requirements(ingredients: Set[str]) -> Set[str]:
techs = recursively_get_unlocking_technologies("rocket-silo")
@@ -232,6 +237,8 @@ def get_rocket_requirements(ingredients: Set[str]) -> Set[str]:
return {tech.name for tech in techs}
free_sample_blacklist = all_ingredient_names | {"rocket-part"}
rocket_recipes = {
Options.MaxSciencePack.option_space_science_pack:
{"rocket-control-unit": 10, "low-density-structure": 10, "rocket-fuel": 10},
@@ -247,4 +254,4 @@ rocket_recipes = {
{"electronic-circuit": 10, "stone-brick": 10, "coal": 10},
Options.MaxSciencePack.option_automation_science_pack:
{"copper-cable": 10, "iron-plate": 10, "wood": 10}
}
}
+3 -1
View File
@@ -5,7 +5,7 @@ from .Technologies import tech_table, recipe_sources, technology_table, advancem
all_ingredient_names, required_technologies, get_rocket_requirements, rocket_recipes
from .Shapes import get_shapes
from .Mod import generate_mod
from .Options import factorio_options
class Factorio(World):
game: str = "Factorio"
@@ -80,6 +80,8 @@ class Factorio(World):
world.completion_condition[player] = lambda state: state.has('Victory', player)
options = factorio_options
def set_custom_technologies(world: MultiWorld, player: int):
custom_technologies = {}
allowed_packs = world.max_science_pack[player].get_allowed_packs()
+39
View File
@@ -0,0 +1,39 @@
import typing
from Options import Option, DefaultOnToggle, Toggle
hollow_knight_randomize_options: typing.Dict[str, type(Option)] = {
"RandomizeDreamers": DefaultOnToggle,
"RandomizeSkills": DefaultOnToggle,
"RandomizeCharms": DefaultOnToggle,
"RandomizeKeys": DefaultOnToggle,
"RandomizeGeoChests": Toggle,
"RandomizeMaskShards": DefaultOnToggle,
"RandomizeVesselFragments": DefaultOnToggle,
"RandomizeCharmNotches": Toggle,
"RandomizePaleOre": DefaultOnToggle,
"RandomizeRancidEggs": Toggle,
"RandomizeRelics": DefaultOnToggle,
"RandomizeMaps": Toggle,
"RandomizeStags": Toggle,
"RandomizeGrubs": Toggle,
"RandomizeWhisperingRoots": Toggle,
"RandomizeRocks": Toggle,
"RandomizeSoulTotems": Toggle,
"RandomizePalaceTotems": Toggle,
"RandomizeLoreTablets": Toggle,
"RandomizeLifebloodCocoons": Toggle,
"RandomizeFlames": Toggle
}
hollow_knight_skip_options: typing.Dict[str, type(Option)] = {
"MILDSKIPS": Toggle,
"SPICYSKIPS": Toggle,
"FIREBALLSKIPS": Toggle,
"ACIDSKIPS": Toggle,
"SPIKETUNNELS": Toggle,
"DARKROOMS": Toggle,
"CURSED": Toggle,
"SHADESKIPS": Toggle,
}
hollow_knight_options: typing.Dict[str, type(Option)] = {**hollow_knight_randomize_options,
**hollow_knight_skip_options}
+27
View File
@@ -0,0 +1,27 @@
import typing
from Options import Choice, Option, Toggle
class AdvancementGoal(Choice):
option_few = 0
option_normal = 1
option_many = 2
default = 1
class CombatDifficulty(Choice):
option_easy = 0
option_normal = 1
option_hard = 2
default = 1
minecraft_options: typing.Dict[str, type(Option)] = {
"advancement_goal": AdvancementGoal,
"combat_difficulty": CombatDifficulty,
"include_hard_advancements": Toggle,
"include_insane_advancements": Toggle,
"include_postgame_advancements": Toggle,
"shuffle_structures": Toggle
}
+104 -52
View File
@@ -1,16 +1,16 @@
from ..generic.Rules import set_rule
from .Locations import exclusion_table, events_table
from Options import AdvancementGoal
from BaseClasses import MultiWorld
def set_rules(world, player):
def set_rules(world: MultiWorld, player: int):
def reachable_locations(state):
postgame_advancements = set(exclusion_table['postgame'].keys())
postgame_advancements.add('Free the End')
for event in events_table.keys():
postgame_advancements.add(event)
return [location for location in world.get_locations() if
(player is None or location.player == player) and
return [location for location in world.get_locations() if
(player is None or location.player == player) and
(location.name not in postgame_advancements) and
location.can_reach(state)]
@@ -18,15 +18,19 @@ def set_rules(world, player):
# There are 5 advancements which cannot be included for dragon spawning (4 postgame, Free the End)
# Hence the true maximum is (92 - 5) = 87
goal = int(world.advancement_goal[player].value)
can_complete = lambda state: len(reachable_locations(state)) >= goal and state.can_reach('The End', 'Region', player) and state.can_kill_ender_dragon(player)
can_complete = lambda state: len(reachable_locations(state)) >= goal and state.can_reach('The End', 'Region',
player) and state.can_kill_ender_dragon(
player)
if world.logic[player] != 'nologic':
if world.logic[player] != 'nologic':
world.completion_condition[player] = lambda state: state.has('Victory', player)
set_rule(world.get_entrance("Nether Portal", player), lambda state: state.has('Flint and Steel', player) and
(state.has('Bucket', player) or state.has('Progressive Tools', player, 3)) and
set_rule(world.get_entrance("Nether Portal", player), lambda state: state.has('Flint and Steel', player) and
(state.has('Bucket', player) or state.has(
'Progressive Tools', player, 3)) and
state.has_iron_ingots(player))
set_rule(world.get_entrance("End Portal", player), lambda state: state.enter_stronghold(player) and state.has('3 Ender Pearls', player, 4))
set_rule(world.get_entrance("End Portal", player),
lambda state: state.enter_stronghold(player) and state.has('3 Ender Pearls', player, 4))
set_rule(world.get_entrance("Overworld Structure 1", player), lambda state: state.can_adventure(player))
set_rule(world.get_entrance("Overworld Structure 2", player), lambda state: state.can_adventure(player))
set_rule(world.get_entrance("Nether Structure 1", player), lambda state: state.can_adventure(player))
@@ -48,33 +52,49 @@ def set_rules(world, player):
state.can_reach('Village', 'Region', player) and # Night Vision, Invisibility
state.can_reach('Bring Home the Beacon', 'Location', player)) # Resistance
set_rule(world.get_location("Best Friends Forever", player), lambda state: True)
set_rule(world.get_location("Bring Home the Beacon", player), lambda state: state.can_kill_wither(player) and state.has_diamond_pickaxe(player) and
state.has("Ingot Crafting", player) and state.has("Resource Blocks", player))
set_rule(world.get_location("Not Today, Thank You", player), lambda state: state.has("Shield", player) and state.has_iron_ingots(player))
set_rule(world.get_location("Isn't It Iron Pick", player), lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player))
set_rule(world.get_location("Bring Home the Beacon", player),
lambda state: state.can_kill_wither(player) and state.has_diamond_pickaxe(player) and
state.has("Ingot Crafting", player) and state.has("Resource Blocks", player))
set_rule(world.get_location("Not Today, Thank You", player),
lambda state: state.has("Shield", player) and state.has_iron_ingots(player))
set_rule(world.get_location("Isn't It Iron Pick", player),
lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player))
set_rule(world.get_location("Local Brewery", player), lambda state: state.can_brew_potions(player))
set_rule(world.get_location("The Next Generation", player), lambda state: can_complete(state))
set_rule(world.get_location("Fishy Business", player), lambda state: state.has("Fishing Rod", player))
set_rule(world.get_location("Hot Tourist Destinations", player), lambda state: True)
set_rule(world.get_location("This Boat Has Legs", player), lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and state.has("Fishing Rod", player))
set_rule(world.get_location("This Boat Has Legs", player),
lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and state.has("Fishing Rod",
player))
set_rule(world.get_location("Sniper Duel", player), lambda state: state.has("Archery", player))
set_rule(world.get_location("Nether", player), lambda state: True)
set_rule(world.get_location("Great View From Up Here", player), lambda state: state.basic_combat(player))
set_rule(world.get_location("How Did We Get Here?", player), lambda state: state.can_brew_potions(player) and state.has_gold_ingots(player) and # most effects; Absorption
state.can_reach('End City', 'Region', player) and state.can_reach('The Nether', 'Region', player) and # Levitation; potion ingredients
state.has("Fishing Rod", player) and state.has("Archery", player) and # Pufferfish, Nautilus Shells; spectral arrows
state.can_reach("Bring Home the Beacon", "Location", player) and # Haste
state.can_reach("Hero of the Village", "Location", player)) # Bad Omen, Hero of the Village
set_rule(world.get_location("Bullseye", player), lambda state: state.has("Archery", player) and state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player))
set_rule(world.get_location("How Did We Get Here?", player),
lambda state: state.can_brew_potions(player) and state.has_gold_ingots(
player) and # most effects; Absorption
state.can_reach('End City', 'Region', player) and state.can_reach('The Nether', 'Region',
player) and # Levitation; potion ingredients
state.has("Fishing Rod", player) and state.has("Archery",
player) and # Pufferfish, Nautilus Shells; spectral arrows
state.can_reach("Bring Home the Beacon", "Location", player) and # Haste
state.can_reach("Hero of the Village", "Location", player)) # Bad Omen, Hero of the Village
set_rule(world.get_location("Bullseye", player),
lambda state: state.has("Archery", player) and state.has("Progressive Tools", player,
2) and state.has_iron_ingots(player))
set_rule(world.get_location("Spooky Scary Skeleton", player), lambda state: state.basic_combat(player))
set_rule(world.get_location("Two by Two", player), lambda state: state.has_iron_ingots(player) and state.can_adventure(player)) # shears > seagrass > turtles; nether > striders; gold carrots > horses skips ingots
set_rule(world.get_location("Two by Two", player),
lambda state: state.has_iron_ingots(player) and state.can_adventure(
player)) # shears > seagrass > turtles; nether > striders; gold carrots > horses skips ingots
set_rule(world.get_location("Stone Age", player), lambda state: True)
set_rule(world.get_location("Two Birds, One Arrow", player), lambda state: state.craft_crossbow(player) and state.can_enchant(player))
set_rule(world.get_location("Two Birds, One Arrow", player),
lambda state: state.craft_crossbow(player) and state.can_enchant(player))
set_rule(world.get_location("We Need to Go Deeper", player), lambda state: True)
set_rule(world.get_location("Who's the Pillager Now?", player), lambda state: state.craft_crossbow(player))
set_rule(world.get_location("Getting an Upgrade", player), lambda state: state.has("Progressive Tools", player))
set_rule(world.get_location("Tactical Fishing", player), lambda state: state.has("Bucket", player) and state.has_iron_ingots(player))
set_rule(world.get_location("Zombie Doctor", player), lambda state: state.can_brew_potions(player) and state.has_gold_ingots(player))
set_rule(world.get_location("Tactical Fishing", player),
lambda state: state.has("Bucket", player) and state.has_iron_ingots(player))
set_rule(world.get_location("Zombie Doctor", player),
lambda state: state.can_brew_potions(player) and state.has_gold_ingots(player))
set_rule(world.get_location("The City at the End of the Game", player), lambda state: True)
set_rule(world.get_location("Ice Bucket Challenge", player), lambda state: state.has_diamond_pickaxe(player))
set_rule(world.get_location("Remote Getaway", player), lambda state: True)
@@ -86,59 +106,91 @@ def set_rules(world, player):
state.can_use_anvil(player) and state.can_enchant(player))
set_rule(world.get_location("The End... Again...", player), lambda state: can_complete(state))
set_rule(world.get_location("Acquire Hardware", player), lambda state: state.has_iron_ingots(player))
set_rule(world.get_location("Not Quite \"Nine\" Lives", player), lambda state: state.can_piglin_trade(player) and state.has("Resource Blocks", player))
set_rule(world.get_location("Cover Me With Diamonds", player), lambda state: state.has("Progressive Armor", player, 2) and state.can_reach("Diamonds!", "Location", player))
set_rule(world.get_location("Not Quite \"Nine\" Lives", player),
lambda state: state.can_piglin_trade(player) and state.has("Resource Blocks", player))
set_rule(world.get_location("Cover Me With Diamonds", player),
lambda state: state.has("Progressive Armor", player, 2) and state.can_reach("Diamonds!", "Location",
player))
set_rule(world.get_location("Sky's the Limit", player), lambda state: state.basic_combat(player))
set_rule(world.get_location("Hired Help", player), lambda state: state.has("Resource Blocks", player) and state.has_iron_ingots(player))
set_rule(world.get_location("Hired Help", player),
lambda state: state.has("Resource Blocks", player) and state.has_iron_ingots(player))
set_rule(world.get_location("Return to Sender", player), lambda state: True)
set_rule(world.get_location("Sweet Dreams", player), lambda state: state.has("Bed", player) or state.can_reach('Village', 'Region', player))
set_rule(world.get_location("You Need a Mint", player), lambda state: can_complete(state) and state.has_bottle_mc(player))
set_rule(world.get_location("Sweet Dreams", player),
lambda state: state.has("Bed", player) or state.can_reach('Village', 'Region', player))
set_rule(world.get_location("You Need a Mint", player),
lambda state: can_complete(state) and state.has_bottle_mc(player))
set_rule(world.get_location("Adventure", player), lambda state: True)
set_rule(world.get_location("Monsters Hunted", player), lambda state: can_complete(state) and state.can_kill_wither(player) and state.has("Fishing Rod", player)) # pufferfish for Water Breathing
set_rule(world.get_location("Monsters Hunted", player),
lambda state: can_complete(state) and state.can_kill_wither(player) and state.has("Fishing Rod",
player)) # pufferfish for Water Breathing
set_rule(world.get_location("Enchanter", player), lambda state: state.can_enchant(player))
set_rule(world.get_location("Voluntary Exile", player), lambda state: state.basic_combat(player))
set_rule(world.get_location("Eye Spy", player), lambda state: state.enter_stronghold(player))
set_rule(world.get_location("The End", player), lambda state: True)
set_rule(world.get_location("Serious Dedication", player), lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots(player))
set_rule(world.get_location("Serious Dedication", player),
lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots(
player))
set_rule(world.get_location("Postmortal", player), lambda state: state.complete_raid(player))
set_rule(world.get_location("Monster Hunter", player), lambda state: True)
set_rule(world.get_location("Adventuring Time", player), lambda state: state.can_adventure(player))
set_rule(world.get_location("A Seedy Place", player), lambda state: True)
set_rule(world.get_location("Those Were the Days", player), lambda state: True)
set_rule(world.get_location("Hero of the Village", player), lambda state: state.complete_raid(player))
set_rule(world.get_location("Hidden in the Depths", player), lambda state: state.can_brew_potions(player) and state.has("Bed", player) and state.has_diamond_pickaxe(player)) # bed mining :)
set_rule(world.get_location("Beaconator", player), lambda state: state.can_kill_wither(player) and state.has_diamond_pickaxe(player) and
state.has("Ingot Crafting", player) and state.has("Resource Blocks", player))
set_rule(world.get_location("Hidden in the Depths", player),
lambda state: state.can_brew_potions(player) and state.has("Bed", player) and state.has_diamond_pickaxe(
player)) # bed mining :)
set_rule(world.get_location("Beaconator", player),
lambda state: state.can_kill_wither(player) and state.has_diamond_pickaxe(player) and
state.has("Ingot Crafting", player) and state.has("Resource Blocks", player))
set_rule(world.get_location("Withering Heights", player), lambda state: state.can_kill_wither(player))
set_rule(world.get_location("A Balanced Diet", player), lambda state: state.has_bottle_mc(player) and state.has_gold_ingots(player) and # honey bottle; gapple
state.has("Resource Blocks", player) and state.can_reach('The End', 'Region', player)) # notch apple, chorus fruit
set_rule(world.get_location("A Balanced Diet", player),
lambda state: state.has_bottle_mc(player) and state.has_gold_ingots(player) and # honey bottle; gapple
state.has("Resource Blocks", player) and state.can_reach('The End', 'Region',
player)) # notch apple, chorus fruit
set_rule(world.get_location("Subspace Bubble", player), lambda state: state.has_diamond_pickaxe(player))
set_rule(world.get_location("Husbandry", player), lambda state: True)
set_rule(world.get_location("Country Lode, Take Me Home", player), lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots(player))
set_rule(world.get_location("Bee Our Guest", player), lambda state: state.has("Campfire", player) and state.has_bottle_mc(player))
set_rule(world.get_location("Country Lode, Take Me Home", player),
lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots(
player))
set_rule(world.get_location("Bee Our Guest", player),
lambda state: state.has("Campfire", player) and state.has_bottle_mc(player))
set_rule(world.get_location("What a Deal!", player), lambda state: True)
set_rule(world.get_location("Uneasy Alliance", player), lambda state: state.has_diamond_pickaxe(player) and state.has('Fishing Rod', player))
set_rule(world.get_location("Diamonds!", player), lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player))
set_rule(world.get_location("A Terrible Fortress", player), lambda state: True) # since you don't have to fight anything
set_rule(world.get_location("A Throwaway Joke", player), lambda state: True) # kill drowned
set_rule(world.get_location("Uneasy Alliance", player),
lambda state: state.has_diamond_pickaxe(player) and state.has('Fishing Rod', player))
set_rule(world.get_location("Diamonds!", player),
lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player))
set_rule(world.get_location("A Terrible Fortress", player),
lambda state: True) # since you don't have to fight anything
set_rule(world.get_location("A Throwaway Joke", player), lambda state: True) # kill drowned
set_rule(world.get_location("Minecraft", player), lambda state: True)
set_rule(world.get_location("Sticky Situation", player), lambda state: state.has("Campfire", player) and state.has_bottle_mc(player))
set_rule(world.get_location("Sticky Situation", player),
lambda state: state.has("Campfire", player) and state.has_bottle_mc(player))
set_rule(world.get_location("Ol' Betsy", player), lambda state: state.craft_crossbow(player))
set_rule(world.get_location("Cover Me in Debris", player), lambda state: state.has("Progressive Armor", player, 2) and
state.has("8 Netherite Scrap", player, 2) and state.has("Ingot Crafting", player) and
state.can_reach("Diamonds!", "Location", player) and state.can_reach("Hidden in the Depths", "Location", player))
set_rule(world.get_location("Cover Me in Debris", player),
lambda state: state.has("Progressive Armor", player, 2) and
state.has("8 Netherite Scrap", player, 2) and state.has("Ingot Crafting", player) and
state.can_reach("Diamonds!", "Location", player) and state.can_reach("Hidden in the Depths",
"Location", player))
set_rule(world.get_location("The End?", player), lambda state: True)
set_rule(world.get_location("The Parrots and the Bats", player), lambda state: True)
set_rule(world.get_location("A Complete Catalogue", player), lambda state: True) # kill fish for raw
set_rule(world.get_location("A Complete Catalogue", player), lambda state: True) # kill fish for raw
set_rule(world.get_location("Getting Wood", player), lambda state: True)
set_rule(world.get_location("Time to Mine!", player), lambda state: True)
set_rule(world.get_location("Hot Topic", player), lambda state: state.has("Ingot Crafting", player))
set_rule(world.get_location("Bake Bread", player), lambda state: True)
set_rule(world.get_location("The Lie", player), lambda state: state.has_iron_ingots(player) and state.has("Bucket", player))
set_rule(world.get_location("On a Rail", player), lambda state: state.has_iron_ingots(player) and state.has('Progressive Tools', player, 2)) # powered rails
set_rule(world.get_location("The Lie", player),
lambda state: state.has_iron_ingots(player) and state.has("Bucket", player))
set_rule(world.get_location("On a Rail", player),
lambda state: state.has_iron_ingots(player) and state.has('Progressive Tools', player, 2)) # powered rails
set_rule(world.get_location("Time to Strike!", player), lambda state: True)
set_rule(world.get_location("Cow Tipper", player), lambda state: True)
set_rule(world.get_location("When Pigs Fly", player), lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and state.has("Fishing Rod", player) and state.can_adventure(player))
set_rule(world.get_location("Overkill", player), lambda state: state.can_brew_potions(player) and (state.has("Progressive Weapons", player) or state.can_reach('The Nether', 'Region', player))) # strength 1 + stone axe crit OR strength 2 + wood axe crit
set_rule(world.get_location("When Pigs Fly", player),
lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and state.has("Fishing Rod",
player) and state.can_adventure(
player))
set_rule(world.get_location("Overkill", player), lambda state: state.can_brew_potions(player) and (
state.has("Progressive Weapons", player) or state.can_reach('The Nether', 'Region',
player))) # strength 1 + stone axe crit OR strength 2 + wood axe crit
set_rule(world.get_location("Librarian", player), lambda state: state.has("Enchanting", player))
set_rule(world.get_location("Overpowered", player), lambda state: state.has("Resource Blocks", player) and state.has_gold_ingots(player))
set_rule(world.get_location("Overpowered", player),
lambda state: state.has("Resource Blocks", player) and state.has_gold_ingots(player))
+2 -1
View File
@@ -4,13 +4,14 @@ from .Regions import mc_regions, link_minecraft_structures
from .Rules import set_rules
from BaseClasses import Region, Entrance
from Options import minecraft_options
from .Options import minecraft_options
from ..AutoWorld import World
client_version = (0, 3)
class MinecraftWorld(World):
game: str = "Minecraft"
options = minecraft_options
def _get_mc_data(self):