Core: make early_items internal only (#1177)
Co-authored-by: beauxq <beauxq@yahoo.com>
This commit is contained in:
parent
4d79920fa6
commit
edd1fff4b7
|
@ -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
46
Fill.py
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()]
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue