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:
parent
72c53513f8
commit
4e3d396394
|
@ -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)
|
||||||
|
@ -336,6 +345,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
|
||||||
|
|
||||||
def collect(self, state: "CollectionState", item: "Item") -> bool:
|
def collect(self, state: "CollectionState", item: "Item") -> bool:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue