Core: make early_items internal only ()

Co-authored-by: beauxq <beauxq@yahoo.com>
This commit is contained in:
espeon65536 2022-11-16 10:32:33 -06:00 committed by GitHub
parent 4d79920fa6
commit edd1fff4b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 61 additions and 76 deletions

View File

@ -48,7 +48,8 @@ class MultiWorld():
state: CollectionState
accessibility: Dict[int, Options.Accessibility]
early_items: Dict[int, Options.EarlyItems]
early_items: Dict[int, Dict[str, int]]
local_early_items: Dict[int, Dict[str, int]]
local_items: Dict[int, Options.LocalItems]
non_local_items: Dict[int, Options.NonLocalItems]
progression_balancing: Dict[int, Options.ProgressionBalancing]
@ -94,6 +95,8 @@ class MultiWorld():
self.customitemarray = []
self.shuffle_ganon = True
self.spoiler = Spoiler(self)
self.early_items = {player: {} for player in self.player_ids}
self.local_early_items = {player: {} for player in self.player_ids}
self.indirect_connections = {}
self.fix_trock_doors = self.AttributeProxy(
lambda player: self.shuffle[player] != 'vanilla' or self.mode[player] == 'inverted')

46
Fill.py
View File

@ -254,14 +254,17 @@ def distribute_early_items(world: MultiWorld,
""" returns new fill_locations and itempool """
early_items_count: typing.Dict[typing.Tuple[str, int], int] = {}
for player in world.player_ids:
for item, count in world.early_items[player].value.items():
early_items_count[(item, player)] = count
items = itertools.chain(world.early_items[player], world.local_early_items[player])
for item in items:
early_items_count[(item, player)] = [world.early_items[player].get(item, 0), world.local_early_items[player].get(item, 0)]
if early_items_count:
early_locations: typing.List[Location] = []
early_priority_locations: typing.List[Location] = []
loc_indexes_to_remove: typing.Set[int] = set()
base_state = world.state.copy()
base_state.sweep_for_events(locations=(loc for loc in world.get_filled_locations() if loc.address is None))
for i, loc in enumerate(fill_locations):
if loc.can_reach(world.state):
if loc.can_reach(base_state):
if loc.progress_type == LocationProgressType.PRIORITY:
early_priority_locations.append(loc)
else:
@ -271,27 +274,48 @@ def distribute_early_items(world: MultiWorld,
early_prog_items: typing.List[Item] = []
early_rest_items: typing.List[Item] = []
early_local_prog_items: typing.Dict[int, typing.List[Item]] = {player: [] for player in world.player_ids}
early_local_rest_items: typing.Dict[int, typing.List[Item]] = {player: [] for player in world.player_ids}
item_indexes_to_remove: typing.Set[int] = set()
for i, item in enumerate(itempool):
if (item.name, item.player) in early_items_count:
if item.advancement:
early_prog_items.append(item)
if early_items_count[(item.name, item.player)][1]:
early_local_prog_items[item.player].append(item)
early_items_count[(item.name, item.player)][1] -= 1
else:
early_prog_items.append(item)
early_items_count[(item.name, item.player)][0] -= 1
else:
early_rest_items.append(item)
if early_items_count[(item.name, item.player)][1]:
early_local_rest_items[item.player].append(item)
early_items_count[(item.name, item.player)][1] -= 1
else:
early_rest_items.append(item)
early_items_count[(item.name, item.player)][0] -= 1
item_indexes_to_remove.add(i)
early_items_count[(item.name, item.player)] -= 1
if early_items_count[(item.name, item.player)] == 0:
if early_items_count[(item.name, item.player)] == [0, 0]:
del early_items_count[(item.name, item.player)]
if len(early_items_count) == 0:
break
itempool = [item for i, item in enumerate(itempool) if i not in item_indexes_to_remove]
fill_restrictive(world, world.state, early_locations, early_rest_items, lock=True)
for player in world.player_ids:
fill_restrictive(world, base_state,
[loc for loc in early_locations if loc.player == player],
early_local_rest_items[player], lock=True)
early_locations = [loc for loc in early_locations if not loc.item]
fill_restrictive(world, base_state, early_locations, early_rest_items, lock=True)
early_locations += early_priority_locations
fill_restrictive(world, world.state, early_locations, early_prog_items, lock=True)
for player in world.player_ids:
fill_restrictive(world, base_state,
[loc for loc in early_locations if loc.player == player],
early_local_prog_items[player], lock=True)
early_locations = [loc for loc in early_locations if not loc.item]
fill_restrictive(world, base_state, early_locations, early_prog_items, lock=True)
unplaced_early_items = early_rest_items + early_prog_items
if unplaced_early_items:
logging.warning(f"Ran out of early locations for early items. Failed to place \
{len(unplaced_early_items)} items early.")
logging.warning("Ran out of early locations for early items. Failed to place "
f"{len(unplaced_early_items)} items early.")
itempool += unplaced_early_items
fill_locations.extend(early_locations)

View File

@ -883,11 +883,6 @@ class NonLocalItems(ItemSet):
display_name = "Not Local Items"
class EarlyItems(ItemDict):
"""Force the specified items to be in locations that are reachable from the start."""
display_name = "Early Items"
class StartInventory(ItemDict):
"""Start with these items."""
verify_item_name = True
@ -986,7 +981,6 @@ per_game_common_options = {
**common_options, # can be overwritten per-game
"local_items": LocalItems,
"non_local_items": NonLocalItems,
"early_items": EarlyItems,
"start_inventory": StartInventory,
"start_hints": StartHints,
"start_location_hints": StartLocationHints,

View File

@ -628,9 +628,9 @@ class TestDistributeItemsRestrictive(unittest.TestCase):
mw = generate_multi_world(2)
player1 = generate_player_data(mw, 1, location_count=5, basic_item_count=5)
player2 = generate_player_data(mw, 2, location_count=5, basic_item_count=5)
mw.early_items[1].value[player1.basic_items[0].name] = 1
mw.early_items[2].value[player2.basic_items[2].name] = 1
mw.early_items[2].value[player2.basic_items[3].name] = 1
mw.early_items[1][player1.basic_items[0].name] = 1
mw.early_items[2][player2.basic_items[2].name] = 1
mw.early_items[2][player2.basic_items[3].name] = 1
early_items = [
player1.basic_items[0],

View File

@ -106,7 +106,7 @@ settings. If a game can be rolled it **must** have a settings section even if it
Some options in Archipelago can be used by every game but must still be placed within the relevant game's section.
Currently, these options are `start_inventory`, `early_items`, `start_hints`, `local_items`, `non_local_items`, `start_location_hints`
Currently, these options are `start_inventory`, `start_hints`, `local_items`, `non_local_items`, `start_location_hints`
, `exclude_locations`, and various plando options.
See the plando guide for more info on plando options. Plando
@ -115,8 +115,6 @@ guide: [Archipelago Plando Guide](/tutorial/Archipelago/plando/en)
* `start_inventory` will give any items defined here to you at the beginning of your game. The format for this must be
the name as it appears in the game files and the amount you would like to start with. For example `Rupees(5): 6` which
will give you 30 rupees.
* `early_items` is formatted in the same way as `start_inventory` and will force the number of each item specified to be
forced into locations that are reachable from the start, before obtaining any items.
* `start_hints` gives you free server hints for the defined item/s at the beginning of the game allowing you to hint for
the location without using any hint points.
* `local_items` will force any items you want to be in your world instead of being in another world.
@ -174,8 +172,6 @@ A Link to the Past:
- Quake
non_local_items:
- Moon Pearl
early_items:
Flute: 1
start_location_hints:
- Spike Cave
priority_locations:
@ -239,9 +235,6 @@ Timespinner:
* `local_items` forces the `Bombos`, `Ether`, and `Quake` medallions to all be placed within our own world, meaning we
have to find it ourselves.
* `non_local_items` forces the `Moon Pearl` to be placed in someone else's world, meaning we won't be able to find it.
* `early_items` forces the `Flute` to be placed in a location that is available from the beginning of the game ("Sphere
1"). Since it is not specified in `local_items` or `non_local_items`, it can be placed one of these locations in any
world.
* `start_location_hints` gives us a starting hint for the `Spike Cave` location available at the beginning of the
multiworld that can be used for no cost.
* `priority_locations` forces a progression item to be placed on the `Link's House` location.

View File

@ -41,7 +41,6 @@ class RLWorld(World):
location_name_to_id = {name: data.code for name, data in location_table.items()}
item_pool: List[RLItem] = []
prefill_items: List[RLItem] = []
def setting(self, name: str):
return getattr(self.multiworld, name)[self.player]
@ -50,8 +49,6 @@ class RLWorld(World):
return {option_name: self.setting(option_name).value for option_name in rl_options}
def generate_early(self):
self.prefill_items = []
# Check validation of names.
additional_lady_names = len(self.setting("additional_lady_names").value)
additional_sir_names = len(self.setting("additional_sir_names").value)
@ -68,10 +65,11 @@ class RLWorld(World):
f"Expected {int(self.setting('number_of_children'))}, Got {additional_sir_names}")
if self.setting("vendors") == "early":
self.prefill_items += [self.create_item("Blacksmith"), self.create_item("Enchantress")]
self.multiworld.local_early_items[self.player]["Blacksmith"] = 1
self.multiworld.local_early_items[self.player]["Enchantress"] = 1
if self.setting("architect") == "early":
self.prefill_items += [self.create_item("Architect")]
self.multiworld.local_early_items[self.player]["Architect"] = 1
def generate_basic(self):
self.item_pool = []
@ -83,7 +81,7 @@ class RLWorld(World):
# Architect
if name == "Architect":
if self.setting("architect") == "disabled" or self.setting("architect") == "early":
if self.setting("architect") == "disabled":
continue
if self.setting("architect") == "start_unlocked":
self.multiworld.push_precollected(self.create_item(name))
@ -94,8 +92,6 @@ class RLWorld(World):
if self.setting("vendors") == "start_unlocked":
self.multiworld.push_precollected(self.create_item(name))
continue
if self.setting("vendors") == "early":
continue
# Haggling
if name == "Haggling" and self.setting("disable_charon"):
@ -192,21 +188,11 @@ class RLWorld(World):
self.item_pool += [self.create_item(name) for _ in range(0, quantity)]
# Fill any empty locations with filler items.
while len(self.item_pool) + len(self.prefill_items) < total_locations:
while len(self.item_pool) < total_locations:
self.item_pool.append(self.create_item(self.get_filler_item_name()))
self.multiworld.itempool += self.item_pool
def pre_fill(self) -> None:
reachable = [loc for loc in self.multiworld.get_reachable_locations(player=self.player) if not loc.item]
self.multiworld.random.shuffle(reachable)
items = self.prefill_items.copy()
for item in items:
reachable.pop().place_locked_item(item)
def get_pre_fill_items(self) -> List[RLItem]:
return self.prefill_items
def get_filler_item_name(self) -> str:
fillers = get_items_by_category("Filler")
weights = [data.weight for data in fillers.values()]

View File

@ -127,7 +127,7 @@ class SMWorld(World):
self.multiworld.local_items[self.player].value.add('No Energy')
if (self.variaRando.args.morphPlacement == "early"):
self.multiworld.local_items[self.player].value.add('Morph')
self.multiworld.local_early_items[self.player]['Morph Ball'] = 1
self.remote_items = self.multiworld.remote_items[self.player]
@ -658,21 +658,6 @@ class SMWorld(World):
return "Nothing"
def pre_fill(self):
if (self.variaRando.args.morphPlacement == "early") and next((item for item in self.multiworld.itempool if item.player == self.player and item.name == "Morph Ball"), False):
viable = []
for location in self.multiworld.get_locations():
if location.player == self.player \
and location.item is None \
and location.can_reach(self.multiworld.state):
viable.append(location)
self.multiworld.random.shuffle(viable)
key = self.multiworld.create_item("Morph Ball", self.player)
loc = viable.pop()
loc.place_locked_item(key)
self.multiworld.itempool[:] = [item for item in self.multiworld.itempool if
item.player != self.player or
item.name != "Morph Ball"]
if len(self.NothingPool) > 0:
nonChozoLoc = []
chozoLoc = []

View File

@ -47,8 +47,7 @@ class SubnauticaWorld(World):
creatures_to_scan: List[str]
def generate_early(self) -> None:
if "Seaglide Fragment" not in self.multiworld.early_items[self.player]:
self.multiworld.early_items[self.player].value["Seaglide Fragment"] = 2
self.multiworld.local_early_items[self.player]["Seaglide Fragment"] = 2
scan_option: Options.AggressiveScanLogic = self.multiworld.creature_scan_logic[self.player]
creature_pool = scan_option.get_pool()

View File

@ -49,15 +49,6 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
The [player settings page](/games/Zillion/player-settings) on the website allows you to configure your personal settings and export a config file from
them.
### Advanced settings
The [advanced settings page](/tutorial/Archipelago/advanced_settings/en) describes more options you can put in your configuration file.
- A recommended setting for Zillion is:
```
early_items:
Scope: 1
```
### Verifying your config file
If you would like to validate your config file to make sure it works, you may do so on the [YAML Validator page](/mysterycheck).

View File

@ -192,6 +192,11 @@ class ZillionRedIDCardCount(Range):
display_name = "Red ID Card count"
class ZillionEarlyScope(Toggle):
""" make sure Scope is available early """
display_name = "early scope"
class ZillionSkill(Range):
""" the difficulty level of the game """
range_start = 0
@ -236,6 +241,7 @@ zillion_options: Dict[str, AssembleOptions] = {
"floppy_disk_count": ZillionFloppyDiskCount,
"scope_count": ZillionScopeCount,
"red_id_card_count": ZillionRedIDCardCount,
"early_scope": ZillionEarlyScope,
"skill": ZillionSkill,
"starting_cards": ZillionStartingCards,
"room_gen": ZillionRoomGen,
@ -352,6 +358,10 @@ def validate(world: "MultiWorld", p: int) -> "Tuple[ZzOptions, Counter[str]]":
room_gen = cast(ZillionRoomGen, wo.room_gen[p])
early_scope = cast(ZillionEarlyScope, wo.early_scope[p])
if early_scope:
world.early_items[p]["Scope"] = 1
zz_item_counts = convert_item_counts(item_counts)
zz_op = ZzOptions(
zz_item_counts,
@ -365,7 +375,7 @@ def validate(world: "MultiWorld", p: int) -> "Tuple[ZzOptions, Counter[str]]":
floppy_req.value,
wo.continues[p].value,
wo.randomize_alarms[p].value,
False, # early scope can be done with AP early_items
False, # early scope is done with AP early_items API
True, # balance defense
starting_cards.value,
bool(room_gen.value)