The Messenger: Fix precollected notes not being removed from the itempool (#3066)

* The Messenger: fix precollected notes not being properly removed from pool

* The Messenger: bump required client version
This commit is contained in:
Aaron Wagener 2024-03-31 10:47:11 -05:00 committed by GitHub
parent 72c53513f8
commit 4e3d396394
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 10 deletions

View File

@ -1,4 +1,5 @@
import logging import logging
from datetime import date
from typing import Any, ClassVar, Dict, List, Optional, TextIO from typing import Any, ClassVar, Dict, List, Optional, TextIO
from BaseClasses import CollectionState, Entrance, Item, ItemClassification, MultiWorld, Tutorial from BaseClasses import CollectionState, Entrance, Item, ItemClassification, MultiWorld, Tutorial
@ -9,7 +10,8 @@ from worlds.AutoWorld import WebWorld, World
from worlds.LauncherComponents import Component, Type, components from worlds.LauncherComponents import Component, Type, components
from .client_setup import launch_game from .client_setup import launch_game
from .connections import CONNECTIONS, RANDOMIZED_CONNECTIONS, TRANSITIONS from .connections import CONNECTIONS, RANDOMIZED_CONNECTIONS, TRANSITIONS
from .constants import ALL_ITEMS, ALWAYS_LOCATIONS, BOSS_LOCATIONS, FILLER, NOTES, PHOBEKINS, PROG_ITEMS, USEFUL_ITEMS from .constants import ALL_ITEMS, ALWAYS_LOCATIONS, BOSS_LOCATIONS, FILLER, NOTES, PHOBEKINS, PROG_ITEMS, TRAPS, \
USEFUL_ITEMS
from .options import AvailablePortals, Goal, Logic, MessengerOptions, NotesNeeded, ShuffleTransitions from .options import AvailablePortals, Goal, Logic, MessengerOptions, NotesNeeded, ShuffleTransitions
from .portals import PORTALS, add_closed_portal_reqs, disconnect_portals, shuffle_portals, validate_portals from .portals import PORTALS, add_closed_portal_reqs, disconnect_portals, shuffle_portals, validate_portals
from .regions import LEVELS, MEGA_SHARDS, LOCATIONS, REGION_CONNECTIONS from .regions import LEVELS, MEGA_SHARDS, LOCATIONS, REGION_CONNECTIONS
@ -110,7 +112,7 @@ class MessengerWorld(World):
}, },
} }
required_client_version = (0, 4, 3) required_client_version = (0, 4, 4)
web = MessengerWeb() web = MessengerWeb()
@ -127,6 +129,7 @@ class MessengerWorld(World):
portal_mapping: List[int] portal_mapping: List[int]
transitions: List[Entrance] transitions: List[Entrance]
reachable_locs: int = 0 reachable_locs: int = 0
filler: Dict[str, int]
def generate_early(self) -> None: def generate_early(self) -> None:
if self.options.goal == Goal.option_power_seal_hunt: if self.options.goal == Goal.option_power_seal_hunt:
@ -146,8 +149,9 @@ class MessengerWorld(World):
self.starting_portals = [f"{portal} Portal" self.starting_portals = [f"{portal} Portal"
for portal in starting_portals[:3] + for portal in starting_portals[:3] +
self.random.sample(starting_portals[3:], k=self.options.available_portals - 3)] self.random.sample(starting_portals[3:], k=self.options.available_portals - 3)]
# super complicated method for adding searing crags to starting portals if it wasn't chosen # super complicated method for adding searing crags to starting portals if it wasn't chosen
# need to add a check for transition shuffle when that gets added back in # TODO add a check for transition shuffle when that gets added back in
if not self.options.shuffle_portals and "Searing Crags Portal" not in self.starting_portals: if not self.options.shuffle_portals and "Searing Crags Portal" not in self.starting_portals:
self.starting_portals.append("Searing Crags Portal") self.starting_portals.append("Searing Crags Portal")
if len(self.starting_portals) > 4: if len(self.starting_portals) > 4:
@ -155,6 +159,10 @@ class MessengerWorld(World):
if portal in self.starting_portals] if portal in self.starting_portals]
self.starting_portals.remove(self.random.choice(portals_to_strip)) self.starting_portals.remove(self.random.choice(portals_to_strip))
self.filler = FILLER.copy()
if (not hasattr(self.options, "traps") and date.today() < date(2024, 4, 2)) or self.options.traps:
self.filler.update(TRAPS)
self.plando_portals = [] self.plando_portals = []
self.portal_mapping = [] self.portal_mapping = []
self.spoiler_portal_mapping = {} self.spoiler_portal_mapping = {}
@ -182,12 +190,13 @@ class MessengerWorld(World):
def create_items(self) -> None: def create_items(self) -> None:
# create items that are always in the item pool # create items that are always in the item pool
main_movement_items = ["Rope Dart", "Wingsuit"] main_movement_items = ["Rope Dart", "Wingsuit"]
precollected_names = [item.name for item in self.multiworld.precollected_items[self.player]]
itempool: List[MessengerItem] = [ itempool: List[MessengerItem] = [
self.create_item(item) self.create_item(item)
for item in self.item_name_to_id for item in self.item_name_to_id
if "Time Shard" not in item and item not in { if item not in {
"Power Seal", *NOTES, *FIGURINES, *main_movement_items, "Power Seal", *NOTES, *FIGURINES, *main_movement_items,
*{collected_item.name for collected_item in self.multiworld.precollected_items[self.player]}, *precollected_names, *FILLER, *TRAPS,
} }
] ]
@ -199,7 +208,7 @@ class MessengerWorld(World):
if self.options.goal == Goal.option_open_music_box: if self.options.goal == Goal.option_open_music_box:
# make a list of all notes except those in the player's defined starting inventory, and adjust the # make a list of all notes except those in the player's defined starting inventory, and adjust the
# amount we need to put in the itempool and precollect based on that # amount we need to put in the itempool and precollect based on that
notes = [note for note in NOTES if note not in self.multiworld.precollected_items[self.player]] notes = [note for note in NOTES if note not in precollected_names]
self.random.shuffle(notes) self.random.shuffle(notes)
precollected_notes_amount = NotesNeeded.range_end - \ precollected_notes_amount = NotesNeeded.range_end - \
self.options.notes_needed - \ self.options.notes_needed - \
@ -228,8 +237,8 @@ class MessengerWorld(World):
remaining_fill = len(self.multiworld.get_unfilled_locations(self.player)) - len(itempool) remaining_fill = len(self.multiworld.get_unfilled_locations(self.player)) - len(itempool)
if remaining_fill < 10: if remaining_fill < 10:
self._filler_items = self.random.choices( self._filler_items = self.random.choices(
list(FILLER)[2:], list(self.filler)[2:],
weights=list(FILLER.values())[2:], weights=list(self.filler.values())[2:],
k=remaining_fill k=remaining_fill
) )
filler = [self.create_filler() for _ in range(remaining_fill)] filler = [self.create_filler() for _ in range(remaining_fill)]
@ -300,8 +309,8 @@ class MessengerWorld(World):
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
if not getattr(self, "_filler_items", None): if not getattr(self, "_filler_items", None):
self._filler_items = [name for name in self.random.choices( self._filler_items = [name for name in self.random.choices(
list(FILLER), list(self.filler),
weights=list(FILLER.values()), weights=list(self.filler.values()),
k=20 k=20
)] )]
return self._filler_items.pop(0) return self._filler_items.pop(0)
@ -335,6 +344,9 @@ class MessengerWorld(World):
if name in {*USEFUL_ITEMS, *USEFUL_SHOP_ITEMS}: if name in {*USEFUL_ITEMS, *USEFUL_SHOP_ITEMS}:
return ItemClassification.useful return ItemClassification.useful
if name in TRAPS:
return ItemClassification.trap
return ItemClassification.filler return ItemClassification.filler

View File

@ -48,6 +48,11 @@ FILLER = {
"Time Shard (500)": 5, "Time Shard (500)": 5,
} }
TRAPS = {
"Teleport Trap": 5,
"Prophecy Trap": 10,
}
# item_name_to_id needs to be deterministic and match upstream # item_name_to_id needs to be deterministic and match upstream
ALL_ITEMS = [ ALL_ITEMS = [
*NOTES, *NOTES,
@ -71,6 +76,8 @@ ALL_ITEMS = [
*SHOP_ITEMS, *SHOP_ITEMS,
*FIGURINES, *FIGURINES,
"Money Wrench", "Money Wrench",
"Teleport Trap",
"Prophecy Trap",
] ]
# locations # locations

View File

@ -1,4 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from datetime import date
from typing import Dict from typing import Dict
from schema import And, Optional, Or, Schema from schema import And, Optional, Or, Schema
@ -123,6 +124,11 @@ class RequiredSeals(Range):
default = range_end default = range_end
class Traps(Toggle):
"""Whether traps should be included in the itempool."""
display_name = "Include Traps"
class ShopPrices(Range): class ShopPrices(Range):
"""Percentage modifier for shuffled item prices in shops""" """Percentage modifier for shuffled item prices in shops"""
display_name = "Shop Prices Modifier" display_name = "Shop Prices Modifier"
@ -199,3 +205,6 @@ class MessengerOptions(DeathLinkMixin, PerGameCommonOptions):
percent_seals_required: RequiredSeals percent_seals_required: RequiredSeals
shop_price: ShopPrices shop_price: ShopPrices
shop_price_plan: PlannedShopPrices shop_price_plan: PlannedShopPrices
if date.today() > date(2024, 4, 1):
traps: Traps