Factorio, Minecraft & Hollow Knight: add startinventory support

This commit is contained in:
Fabian Dill 2021-05-09 21:22:21 +02:00
parent 382c6d0445
commit 909172cbad
10 changed files with 40 additions and 57 deletions

View File

@ -68,6 +68,7 @@ class MultiWorld():
self.fix_palaceofdarkness_exit = self.AttributeProxy(lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple']) self.fix_palaceofdarkness_exit = self.AttributeProxy(lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'])
self.fix_trock_exit = self.AttributeProxy(lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple']) self.fix_trock_exit = self.AttributeProxy(lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'])
self.NOTCURSED = self.AttributeProxy(lambda player: not self.CURSED[player]) self.NOTCURSED = self.AttributeProxy(lambda player: not self.CURSED[player])
self.remote_items = self.AttributeProxy(lambda player: self.game[player] != "A Link to the Past")
for player in range(1, players + 1): for player in range(1, players + 1):
def set_player_attr(attr, val): def set_player_attr(attr, val):
@ -87,7 +88,6 @@ class MultiWorld():
set_player_attr('retro', False) set_player_attr('retro', False)
set_player_attr('hints', True) set_player_attr('hints', True)
set_player_attr('player_names', []) set_player_attr('player_names', [])
set_player_attr('remote_items', False)
set_player_attr('required_medallions', ['Ether', 'Quake']) set_player_attr('required_medallions', ['Ether', 'Quake'])
set_player_attr('swamp_patch_required', False) set_player_attr('swamp_patch_required', False)
set_player_attr('powder_patch_required', False) set_player_attr('powder_patch_required', False)

18
Main.py
View File

@ -29,7 +29,7 @@ from worlds.factorio.Mod import generate_mod
from worlds.minecraft import gen_minecraft, fill_minecraft_slot_data, generate_mc_data from worlds.minecraft import gen_minecraft, fill_minecraft_slot_data, generate_mc_data
from worlds.minecraft.Regions import minecraft_create_regions from worlds.minecraft.Regions import minecraft_create_regions
from worlds.generic.Rules import locality_rules from worlds.generic.Rules import locality_rules
from worlds import Games from worlds import Games, lookup_any_item_name_to_id
import Patch import Patch
seeddigits = 20 seeddigits = 20
@ -89,7 +89,6 @@ def main(args, seed=None):
world.hints = args.hints.copy() world.hints = args.hints.copy()
world.remote_items = args.remote_items.copy()
world.mapshuffle = args.mapshuffle.copy() world.mapshuffle = args.mapshuffle.copy()
world.compassshuffle = args.compassshuffle.copy() world.compassshuffle = args.compassshuffle.copy()
world.keyshuffle = args.keyshuffle.copy() world.keyshuffle = args.keyshuffle.copy()
@ -171,15 +170,15 @@ def main(args, seed=None):
world.player_names[player].append(name) world.player_names[player].append(name)
logger.info('') logger.info('')
for player in world.player_ids:
for item_name in args.startinventory[player]:
item = Item(item_name, True, lookup_any_item_name_to_id[item_name], player)
world.push_precollected(item)
for player in world.alttp_player_ids: for player in world.alttp_player_ids:
world.difficulty_requirements[player] = difficulties[world.difficulty[player]] world.difficulty_requirements[player] = difficulties[world.difficulty[player]]
for player in world.player_ids: for player in world.player_ids:
for tok in filter(None, args.startinventory[player].split(',')):
item = ItemFactory(tok.strip(), player)
if item:
world.push_precollected(item)
# enforce pre-defined local items. # enforce pre-defined local items.
if world.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]: if world.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]:
@ -489,9 +488,9 @@ def main(args, seed=None):
er_hint_data[player][location_id] = main_entrance.name er_hint_data[player][location_id] = main_entrance.name
oldmancaves.append(((location_id, player), (item.code, player))) oldmancaves.append(((location_id, player), (item.code, player)))
precollected_items = [[] for player in range(world.players)] precollected_items = {player: [] for player in range(1, world.players+1)}
for item in world.precollected_items: for item in world.precollected_items:
precollected_items[item.player - 1].append(item.code) precollected_items[item.player].append(item.code)
FillDisabledShopSlots(world) FillDisabledShopSlots(world)
@ -527,8 +526,7 @@ def main(args, seed=None):
"names": parsed_names, "names": parsed_names,
"connect_names": connect_names, "connect_names": connect_names,
"remote_items": {player for player in range(1, world.players + 1) if "remote_items": {player for player in range(1, world.players + 1) if
world.remote_items[player] or world.remote_items[player]},
world.game[player] != "A Link to the Past"},
"locations": { "locations": {
(location.address, location.player): (location.address, location.player):
(location.item.code, location.item.player) (location.item.code, location.item.player)

View File

@ -150,6 +150,12 @@ class Context(Node):
self.er_hint_data = {int(player): {int(address): name for address, name in loc_data.items()} self.er_hint_data = {int(player): {int(address): name for address, name in loc_data.items()}
for player, loc_data in decoded_obj["er_hint_data"].items()} for player, loc_data in decoded_obj["er_hint_data"].items()}
self.games = decoded_obj["games"] self.games = decoded_obj["games"]
# award remote-items start inventory:
for team in range(len(decoded_obj['names'])):
for slot, item_codes in decoded_obj["precollected_items"].items():
if slot in self.remote_items:
self.received_items[team, slot] = [NetworkItem(item_code, -2, 0) for item_code in item_codes]
if use_embedded_server_options: if use_embedded_server_options:
server_options = decoded_obj.get("server_options", {}) server_options = decoded_obj.get("server_options", {})
self._set_options(server_options) self._set_options(server_options)

View File

@ -528,6 +528,17 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
else: else:
raise Exception(f"Could not force item {item} to be world-non-local, as it was not recognized.") raise Exception(f"Could not force item {item} to be world-non-local, as it was not recognized.")
inventoryweights = weights.get('startinventory', {})
startitems = []
for item in inventoryweights.keys():
itemvalue = get_choice(item, inventoryweights)
if isinstance(itemvalue, int):
for i in range(int(itemvalue)):
startitems.append(item)
elif itemvalue:
startitems.append(item)
ret.startinventory = startitems
if ret.game == "A Link to the Past": if ret.game == "A Link to the Past":
roll_alttp_settings(ret, weights, plando_options) roll_alttp_settings(ret, weights, plando_options)
elif ret.game == "Hollow Knight": elif ret.game == "Hollow Knight":
@ -700,23 +711,8 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
if not ret.required_medallions[index]: if not ret.required_medallions[index]:
raise Exception(f"unknown Medallion {medallion} for {'misery mire' if index == 0 else 'turtle rock'}") raise Exception(f"unknown Medallion {medallion} for {'misery mire' if index == 0 else 'turtle rock'}")
inventoryweights = weights.get('startinventory', {})
startitems = []
for item in inventoryweights.keys():
itemvalue = get_choice(item, inventoryweights)
if item.startswith(('Progressive ', 'Small Key ', 'Rupee', 'Piece of Heart', 'Boss Heart Container',
'Sanctuary Heart Container', 'Arrow', 'Bombs ', 'Bomb ', 'Bottle')) and isinstance(
itemvalue, int):
for i in range(int(itemvalue)):
startitems.append(item)
elif itemvalue:
startitems.append(item)
ret.startinventory = ','.join(startitems)
ret.glitch_boots = get_choice('glitch_boots', weights, True) ret.glitch_boots = get_choice('glitch_boots', weights, True)
ret.remote_items = get_choice('remote_items', weights, False)
if get_choice("local_keys", weights, "l" in dungeon_items): if get_choice("local_keys", weights, "l" in dungeon_items):
# () important for ordering of commands, without them the Big Keys section is part of the Small Key else # () important for ordering of commands, without them the Big Keys section is part of the Small Key else
ret.local_items |= item_name_groups["Small Keys"] if ret.keyshuffle else set() ret.local_items |= item_name_groups["Small Keys"] if ret.keyshuffle else set()

View File

@ -351,7 +351,7 @@ def getPlayerTracker(tracker: UUID, tracked_team: int, tracked_player: int):
checks_done = {loc_name: 0 for loc_name in default_locations} checks_done = {loc_name: 0 for loc_name in default_locations}
# Add starting items to inventory # Add starting items to inventory
starting_items = precollected_items[tracked_player - 1] starting_items = precollected_items[tracked_player]
if starting_items: if starting_items:
for item_id in starting_items: for item_id in starting_items:
attribute_item_solo(inventory, item_id) attribute_item_solo(inventory, item_id)
@ -506,7 +506,7 @@ def getTracker(tracker: UUID):
for (team, player), locations_checked in multisave.get("location_checks", {}): for (team, player), locations_checked in multisave.get("location_checks", {}):
if precollected_items: if precollected_items:
precollected = precollected_items[player - 1] precollected = precollected_items[player]
for item_id in precollected: for item_id in precollected:
attribute_item(inventory, team, player, item_id) attribute_item(inventory, team, player, item_id)
for location in locations_checked: for location in locations_checked:

View File

@ -36,6 +36,12 @@ accessibility:
progression_balancing: progression_balancing:
on: 50 # A system to reduce BK, as in times during which you can't do anything by moving your items into an earlier access sphere to make it likely you have stuff to do on: 50 # A system to reduce BK, as in times during which you can't do anything by moving your items into an earlier access sphere to make it likely you have stuff to do
off: 0 # Turn this off if you don't mind a longer multiworld, or can glitch/sequence break around missing items. off: 0 # Turn this off if you don't mind a longer multiworld, or can glitch/sequence break around missing items.
# Can be uncommented to use it
# startinventory: # Begin the file with the listed items/upgrades
# Please only use items for the correct game, use triggers if need to be have seperated lists.
# Pegasus Boots: on
# Bomb Upgrade (+10): 4
# Arrow Upgrade (+10): 4
# Factorio options: # Factorio options:
tech_tree_layout: tech_tree_layout:
single: 1 single: 1
@ -347,11 +353,6 @@ green_clock_time: # For all timer modes, the amount of time in minutes to gain o
# - "Moon Pearl" # - "Moon Pearl"
# - "Small Keys" # - "Small Keys"
# - "Big Keys" # - "Big Keys"
# Can be uncommented to use it
# startinventory: # Begin the file with the listed items/upgrades
# Pegasus Boots: on
# Bomb Upgrade (+10): 4
# Arrow Upgrade (+10): 4
glitch_boots: glitch_boots:
on: 50 # Start with Pegasus Boots in any glitched logic mode that makes use of them on: 50 # Start with Pegasus Boots in any glitched logic mode that makes use of them
off: 0 off: 0

View File

@ -27,7 +27,7 @@ lookup_any_location_name_to_id = {name: id for id, name in lookup_any_location_i
network_data_package = {"lookup_any_location_id_to_name": lookup_any_location_id_to_name, network_data_package = {"lookup_any_location_id_to_name": lookup_any_location_id_to_name,
"lookup_any_item_id_to_name": lookup_any_item_id_to_name, "lookup_any_item_id_to_name": lookup_any_item_id_to_name,
"version": 5} "version": 6}
@enum.unique @enum.unique

View File

@ -352,7 +352,6 @@ def parse_arguments(argv, no_defaults=False):
Torches means additionally easily accessible Torches that can be lit with Fire Rod are considered doable. Torches means additionally easily accessible Torches that can be lit with Fire Rod are considered doable.
None means full traversal through dark rooms without tools is considered doable.''') None means full traversal through dark rooms without tools is considered doable.''')
parser.add_argument('--restrict_dungeon_item_on_boss', default=defval(False), action="store_true") parser.add_argument('--restrict_dungeon_item_on_boss', default=defval(False), action="store_true")
parser.add_argument('--remote_items', default=defval(False), action='store_true')
parser.add_argument('--multi', default=defval(1), type=lambda value: min(max(int(value), 1), 255)) parser.add_argument('--multi', default=defval(1), type=lambda value: min(max(int(value), 1), 255))
parser.add_argument('--names', default=defval('')) parser.add_argument('--names', default=defval(''))
parser.add_argument('--teams', default=defval(1), type=lambda value: max(int(value), 1)) parser.add_argument('--teams', default=defval(1), type=lambda value: max(int(value), 1))
@ -409,7 +408,7 @@ def parse_arguments(argv, no_defaults=False):
"triforce_pieces_required", "shop_shuffle", "shop_shuffle_slots", "triforce_pieces_required", "shop_shuffle", "shop_shuffle_slots",
"required_medallions", "required_medallions",
"plando_items", "plando_texts", "plando_connections", "er_seeds", "plando_items", "plando_texts", "plando_connections", "er_seeds",
'remote_items', 'progressive', 'dungeon_counters', 'glitch_boots', 'killable_thieves', 'progressive', 'dungeon_counters', 'glitch_boots', 'killable_thieves',
'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic', 'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic',
'restrict_dungeon_item_on_boss', 'reduceflashing', 'game', 'restrict_dungeon_item_on_boss', 'reduceflashing', 'game',
'hud_palettes', 'sword_palettes', 'shield_palettes', 'link_palettes', 'triforcehud']: 'hud_palettes', 'sword_palettes', 'shield_palettes', 'link_palettes', 'triforcehud']:

View File

@ -676,10 +676,12 @@ location_table: typing.Dict[str,
from worlds.alttp.Shops import shop_table_by_location_id, shop_table_by_location from worlds.alttp.Shops import shop_table_by_location_id, shop_table_by_location
lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int}
lookup_id_to_name = {**lookup_id_to_name, **{data[1]: name for name, data in key_drop_data.items()}, -1: "cheat console"} lookup_id_to_name = {**lookup_id_to_name, **{data[1]: name for name, data in key_drop_data.items()},
-1: "Cheat Console", -2: "Server"}
lookup_id_to_name.update(shop_table_by_location_id) lookup_id_to_name.update(shop_table_by_location_id)
lookup_name_to_id = {name: data[0] for name, data in location_table.items() if type(data[0]) == int} lookup_name_to_id = {name: data[0] for name, data in location_table.items() if type(data[0]) == int}
lookup_name_to_id = {**lookup_name_to_id, **{name: data[1] for name, data in key_drop_data.items()}, "cheat console": -1} lookup_name_to_id = {**lookup_name_to_id, **{name: data[1] for name, data in key_drop_data.items()},
"Cheat Console": -1, "Server": -2}
lookup_name_to_id.update(shop_table_by_location) lookup_name_to_id.update(shop_table_by_location)
lookup_vanilla_location_to_entrance = {1572883: 'Kings Grave Inner Rocks', 191256: 'Kings Grave Inner Rocks', lookup_vanilla_location_to_entrance = {1572883: 'Kings Grave Inner Rocks', 191256: 'Kings Grave Inner Rocks',

View File

@ -91,25 +91,6 @@ from BaseClasses import Location, Item
# self.dark_room_logic = "lamp" # self.dark_room_logic = "lamp"
# self.restrict_dungeon_item_on_boss = False # self.restrict_dungeon_item_on_boss = False
# #
# @property
# def sewer_light_cone(self):
# return self.mode == "standard"
#
# @property
# def fix_trock_doors(self):
# return self.shuffle != 'vanilla' or self.mode == 'inverted'
#
# @property
# def fix_skullwoods_exit(self):
# return self.shuffle not in {'vanilla', 'simple', 'restricted', 'dungeonssimple'}
#
# @property
# def fix_palaceofdarkness_exit(self):
# return self.shuffle not in {'vanilla', 'simple', 'restricted', 'dungeonssimple'}
#
# @property
# def fix_trock_exit(self):
# return self.shuffle not in {'vanilla', 'simple', 'restricted', 'dungeonssimple'}
class ALttPLocation(Location): class ALttPLocation(Location):