SC2: Greater variety on short generations (#1367)
Originally, short generations used an artificial cull to create balanced mission distributions. This resulted in campaigns that were somewhat too consistent, and on some standard settings combinations, this resulted in campaigns having The Outlaws as the second mission 100% of the time. It also caused generation to fail a bit too easily if the player excluded too many missions. This removes the cull and adds an additional early Easy mission slot to all of the reduced sized campaigns. When playing on No Build settings, this also pushes many of the missions down a difficulty level to ensure greater variety, and pushes additional missions down on Advanced Tactics. Additional small fixes: The in-world Excluded Missions validation check is replaced by the core OptionSet check. Fixed issue with Existing Items not getting their upgrades locked with Units Always Have Upgrades on.
This commit is contained in:
parent
016157a0eb
commit
17e90ce12c
|
@ -52,9 +52,9 @@ class StarcraftClientProcessor(ClientCommandProcessor):
|
|||
"""Overrides the current difficulty set for the seed. Takes the argument casual, normal, hard, or brutal"""
|
||||
options = difficulty.split()
|
||||
num_options = len(options)
|
||||
difficulty_choice = options[0].lower()
|
||||
|
||||
if num_options > 0:
|
||||
difficulty_choice = options[0].lower()
|
||||
if difficulty_choice == "casual":
|
||||
self.ctx.difficulty_override = 0
|
||||
elif difficulty_choice == "normal":
|
||||
|
@ -71,7 +71,11 @@ class StarcraftClientProcessor(ClientCommandProcessor):
|
|||
return True
|
||||
|
||||
else:
|
||||
self.output("Difficulty needs to be specified in the command.")
|
||||
if self.ctx.difficulty == -1:
|
||||
self.output("Please connect to a seed before checking difficulty.")
|
||||
else:
|
||||
self.output("Current difficulty: " + ["Casual", "Normal", "Hard", "Brutal"][self.ctx.difficulty])
|
||||
self.output("To change the difficulty, add the name of the difficulty after the command.")
|
||||
return False
|
||||
|
||||
def _cmd_disable_mission_check(self) -> bool:
|
||||
|
|
|
@ -182,9 +182,11 @@ filler_items: typing.Tuple[str, ...] = (
|
|||
'+15 Starting Vespene'
|
||||
)
|
||||
|
||||
# Defense rating table
|
||||
# Commented defense ratings are handled in LogicMixin
|
||||
defense_ratings = {
|
||||
"Siege Tank": 5,
|
||||
"Maelstrom Rounds": 2,
|
||||
# "Maelstrom Rounds": 2,
|
||||
"Planetary Fortress": 3,
|
||||
# Bunker w/ Marine/Marauder: 3,
|
||||
"Perdition Turret": 2,
|
||||
|
@ -193,7 +195,7 @@ defense_ratings = {
|
|||
}
|
||||
zerg_defense_ratings = {
|
||||
"Perdition Turret": 2,
|
||||
# Bunker w/ Firebat: 2
|
||||
# Bunker w/ Firebat: 2,
|
||||
"Hive Mind Emulator": 3,
|
||||
"Psi Disruptor": 3
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ class SC2WoLLogic(LogicMixin):
|
|||
or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has('Wraith', player)
|
||||
|
||||
def _sc2wol_has_competent_anti_air(self, multiworld: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Marine', 'Goliath'}, player) or self._sc2wol_has_air_anti_air(multiworld, player)
|
||||
return self.has('Goliath', player) \
|
||||
or self.has('Marine', player) and self.has_any({'Medic', 'Medivac'}, player) \
|
||||
or self._sc2wol_has_air_anti_air(multiworld, player)
|
||||
|
||||
def _sc2wol_has_anti_air(self, multiworld: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Missile Turret', 'Thor', 'War Pigs', 'Spartan Company', "Hel's Angel", 'Battlecruiser', 'Wraith'}, player) \
|
||||
return self.has_any({'Missile Turret', 'Thor', 'War Pigs', 'Spartan Company', "Hel's Angel", 'Battlecruiser', 'Marine', 'Wraith'}, player) \
|
||||
or self._sc2wol_has_competent_anti_air(multiworld, player) \
|
||||
or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player)
|
||||
|
||||
|
@ -28,6 +30,8 @@ class SC2WoLLogic(LogicMixin):
|
|||
defense_score = sum((defense_ratings[item] for item in defense_ratings if self.has(item, player)))
|
||||
if self.has_any({'Marine', 'Marauder'}, player) and self.has('Bunker', player):
|
||||
defense_score += 3
|
||||
if self.has_all({'Siege Tank', 'Maelstrom Rounds'}, player):
|
||||
defense_score += 2
|
||||
if zerg_enemy:
|
||||
defense_score += sum((zerg_defense_ratings[item] for item in zerg_defense_ratings if self.has(item, player)))
|
||||
if self.has('Firebat', player) and self.has('Bunker', player):
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
from typing import NamedTuple, Dict, List, Set
|
||||
|
||||
from BaseClasses import MultiWorld
|
||||
from .Options import get_option_value
|
||||
from typing import NamedTuple, Dict, List
|
||||
from enum import IntEnum
|
||||
|
||||
no_build_regions_list = ["Liberation Day", "Breakout", "Ghost of a Chance", "Piercing the Shroud", "Whispers of Doom",
|
||||
"Belly of the Beast"]
|
||||
|
@ -13,6 +11,14 @@ hard_regions_list = ["Maw of the Void", "Engine of Destruction", "In Utter Darkn
|
|||
"Shatter the Sky"]
|
||||
|
||||
|
||||
class MissionPools(IntEnum):
|
||||
STARTER = 0
|
||||
EASY = 1
|
||||
MEDIUM = 2
|
||||
HARD = 3
|
||||
FINAL = 4
|
||||
|
||||
|
||||
class MissionInfo(NamedTuple):
|
||||
id: int
|
||||
required_world: List[int]
|
||||
|
@ -23,119 +29,119 @@ class MissionInfo(NamedTuple):
|
|||
|
||||
|
||||
class FillMission(NamedTuple):
|
||||
type: str
|
||||
type: int
|
||||
connect_to: List[int] # -1 connects to Menu
|
||||
category: str
|
||||
number: int = 0 # number of worlds need beaten
|
||||
completion_critical: bool = False # missions needed to beat game
|
||||
or_requirements: bool = False # true if the requirements should be or-ed instead of and-ed
|
||||
relegate: bool = False # true if this is a slot no build missions should be relegated to.
|
||||
removal_priority: int = 0 # how many missions missing from the pool required to remove this mission
|
||||
|
||||
|
||||
vanilla_shuffle_order = [
|
||||
FillMission("no_build", [-1], "Mar Sara", completion_critical=True),
|
||||
FillMission("easy", [0], "Mar Sara", completion_critical=True),
|
||||
FillMission("easy", [1], "Mar Sara", completion_critical=True),
|
||||
FillMission("easy", [2], "Colonist"),
|
||||
FillMission("medium", [3], "Colonist"),
|
||||
FillMission("hard", [4], "Colonist", number=7),
|
||||
FillMission("hard", [4], "Colonist", number=7, relegate=True),
|
||||
FillMission("easy", [2], "Artifact", completion_critical=True),
|
||||
FillMission("medium", [7], "Artifact", number=8, completion_critical=True),
|
||||
FillMission("hard", [8], "Artifact", number=11, completion_critical=True),
|
||||
FillMission("hard", [9], "Artifact", number=14, completion_critical=True),
|
||||
FillMission("hard", [10], "Artifact", completion_critical=True),
|
||||
FillMission("medium", [2], "Covert", number=4),
|
||||
FillMission("medium", [12], "Covert"),
|
||||
FillMission("hard", [13], "Covert", number=8, relegate=True),
|
||||
FillMission("hard", [13], "Covert", number=8, relegate=True),
|
||||
FillMission("medium", [2], "Rebellion", number=6),
|
||||
FillMission("hard", [16], "Rebellion"),
|
||||
FillMission("hard", [17], "Rebellion"),
|
||||
FillMission("hard", [18], "Rebellion"),
|
||||
FillMission("hard", [19], "Rebellion", relegate=True),
|
||||
FillMission("medium", [8], "Prophecy"),
|
||||
FillMission("hard", [21], "Prophecy"),
|
||||
FillMission("hard", [22], "Prophecy"),
|
||||
FillMission("hard", [23], "Prophecy", relegate=True),
|
||||
FillMission("hard", [11], "Char", completion_critical=True),
|
||||
FillMission("hard", [25], "Char", completion_critical=True),
|
||||
FillMission("hard", [25], "Char", completion_critical=True),
|
||||
FillMission("all_in", [26, 27], "Char", completion_critical=True, or_requirements=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "Mar Sara", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [0], "Mar Sara", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [1], "Mar Sara", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [2], "Colonist"),
|
||||
FillMission(MissionPools.MEDIUM, [3], "Colonist"),
|
||||
FillMission(MissionPools.HARD, [4], "Colonist", number=7),
|
||||
FillMission(MissionPools.HARD, [4], "Colonist", number=7, removal_priority=1),
|
||||
FillMission(MissionPools.EASY, [2], "Artifact", completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [7], "Artifact", number=8, completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [8], "Artifact", number=11, completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [9], "Artifact", number=14, completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [10], "Artifact", completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [2], "Covert", number=4),
|
||||
FillMission(MissionPools.MEDIUM, [12], "Covert"),
|
||||
FillMission(MissionPools.HARD, [13], "Covert", number=8, removal_priority=3),
|
||||
FillMission(MissionPools.HARD, [13], "Covert", number=8, removal_priority=2),
|
||||
FillMission(MissionPools.MEDIUM, [2], "Rebellion", number=6),
|
||||
FillMission(MissionPools.HARD, [16], "Rebellion"),
|
||||
FillMission(MissionPools.HARD, [17], "Rebellion"),
|
||||
FillMission(MissionPools.HARD, [18], "Rebellion"),
|
||||
FillMission(MissionPools.HARD, [19], "Rebellion", removal_priority=5),
|
||||
FillMission(MissionPools.MEDIUM, [8], "Prophecy", removal_priority=9),
|
||||
FillMission(MissionPools.HARD, [21], "Prophecy", removal_priority=8),
|
||||
FillMission(MissionPools.HARD, [22], "Prophecy", removal_priority=7),
|
||||
FillMission(MissionPools.HARD, [23], "Prophecy", removal_priority=6),
|
||||
FillMission(MissionPools.HARD, [11], "Char", completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [25], "Char", completion_critical=True, removal_priority=4),
|
||||
FillMission(MissionPools.HARD, [25], "Char", completion_critical=True),
|
||||
FillMission(MissionPools.FINAL, [26, 27], "Char", completion_critical=True, or_requirements=True)
|
||||
]
|
||||
|
||||
mini_campaign_order = [
|
||||
FillMission("no_build", [-1], "Mar Sara", completion_critical=True),
|
||||
FillMission("easy", [0], "Colonist"),
|
||||
FillMission("medium", [1], "Colonist"),
|
||||
FillMission("medium", [0], "Artifact", completion_critical=True),
|
||||
FillMission("medium", [3], "Artifact", number=4, completion_critical=True),
|
||||
FillMission("hard", [4], "Artifact", number=8, completion_critical=True),
|
||||
FillMission("medium", [0], "Covert", number=2),
|
||||
FillMission("hard", [6], "Covert"),
|
||||
FillMission("medium", [0], "Rebellion", number=3),
|
||||
FillMission("hard", [8], "Rebellion"),
|
||||
FillMission("medium", [4], "Prophecy"),
|
||||
FillMission("hard", [10], "Prophecy"),
|
||||
FillMission("hard", [5], "Char", completion_critical=True),
|
||||
FillMission("hard", [5], "Char", completion_critical=True),
|
||||
FillMission("all_in", [12, 13], "Char", completion_critical=True, or_requirements=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "Mar Sara", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [0], "Colonist"),
|
||||
FillMission(MissionPools.MEDIUM, [1], "Colonist"),
|
||||
FillMission(MissionPools.EASY, [0], "Artifact", completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [3], "Artifact", number=4, completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [4], "Artifact", number=8, completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [0], "Covert", number=2),
|
||||
FillMission(MissionPools.HARD, [6], "Covert"),
|
||||
FillMission(MissionPools.MEDIUM, [0], "Rebellion", number=3),
|
||||
FillMission(MissionPools.HARD, [8], "Rebellion"),
|
||||
FillMission(MissionPools.MEDIUM, [4], "Prophecy"),
|
||||
FillMission(MissionPools.HARD, [10], "Prophecy"),
|
||||
FillMission(MissionPools.HARD, [5], "Char", completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [5], "Char", completion_critical=True),
|
||||
FillMission(MissionPools.FINAL, [12, 13], "Char", completion_critical=True, or_requirements=True)
|
||||
]
|
||||
|
||||
gauntlet_order = [
|
||||
FillMission("no_build", [-1], "I", completion_critical=True),
|
||||
FillMission("easy", [0], "II", completion_critical=True),
|
||||
FillMission("medium", [1], "III", completion_critical=True),
|
||||
FillMission("medium", [2], "IV", completion_critical=True),
|
||||
FillMission("hard", [3], "V", completion_critical=True),
|
||||
FillMission("hard", [4], "VI", completion_critical=True),
|
||||
FillMission("all_in", [5], "Final", completion_critical=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "I", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [0], "II", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [1], "III", completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [2], "IV", completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [3], "V", completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [4], "VI", completion_critical=True),
|
||||
FillMission(MissionPools.FINAL, [5], "Final", completion_critical=True)
|
||||
]
|
||||
|
||||
grid_order = [
|
||||
FillMission("no_build", [-1], "_1"),
|
||||
FillMission("medium", [0], "_1"),
|
||||
FillMission("medium", [1, 6, 3], "_1", or_requirements=True),
|
||||
FillMission("hard", [2, 7], "_1", or_requirements=True),
|
||||
FillMission("easy", [0], "_2"),
|
||||
FillMission("medium", [1, 4], "_2", or_requirements=True),
|
||||
FillMission("hard", [2, 5, 10, 7], "_2", or_requirements=True),
|
||||
FillMission("hard", [3, 6, 11], "_2", or_requirements=True),
|
||||
FillMission("medium", [4, 9, 12], "_3", or_requirements=True),
|
||||
FillMission("hard", [5, 8, 10, 13], "_3", or_requirements=True),
|
||||
FillMission("hard", [6, 9, 11, 14], "_3", or_requirements=True),
|
||||
FillMission("hard", [7, 10], "_3", or_requirements=True),
|
||||
FillMission("hard", [8, 13], "_4", or_requirements=True),
|
||||
FillMission("hard", [9, 12, 14], "_4", or_requirements=True),
|
||||
FillMission("hard", [10, 13], "_4", or_requirements=True),
|
||||
FillMission("all_in", [11, 14], "_4", or_requirements=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "_1"),
|
||||
FillMission(MissionPools.EASY, [0], "_1"),
|
||||
FillMission(MissionPools.MEDIUM, [1, 6, 3], "_1", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [2, 7], "_1", or_requirements=True),
|
||||
FillMission(MissionPools.EASY, [0], "_2"),
|
||||
FillMission(MissionPools.MEDIUM, [1, 4], "_2", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [2, 5, 10, 7], "_2", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [3, 6, 11], "_2", or_requirements=True),
|
||||
FillMission(MissionPools.MEDIUM, [4, 9, 12], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [5, 8, 10, 13], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [6, 9, 11, 14], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [7, 10], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [8, 13], "_4", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [9, 12, 14], "_4", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [10, 13], "_4", or_requirements=True),
|
||||
FillMission(MissionPools.FINAL, [11, 14], "_4", or_requirements=True)
|
||||
]
|
||||
|
||||
mini_grid_order = [
|
||||
FillMission("no_build", [-1], "_1"),
|
||||
FillMission("medium", [0], "_1"),
|
||||
FillMission("medium", [1, 5], "_1", or_requirements=True),
|
||||
FillMission("easy", [0], "_2"),
|
||||
FillMission("medium", [1, 3], "_2", or_requirements=True),
|
||||
FillMission("hard", [2, 4], "_2", or_requirements=True),
|
||||
FillMission("medium", [3, 7], "_3", or_requirements=True),
|
||||
FillMission("hard", [4, 6], "_3", or_requirements=True),
|
||||
FillMission("all_in", [5, 7], "_3", or_requirements=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "_1"),
|
||||
FillMission(MissionPools.EASY, [0], "_1"),
|
||||
FillMission(MissionPools.MEDIUM, [1, 5], "_1", or_requirements=True),
|
||||
FillMission(MissionPools.EASY, [0], "_2"),
|
||||
FillMission(MissionPools.MEDIUM, [1, 3], "_2", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [2, 4], "_2", or_requirements=True),
|
||||
FillMission(MissionPools.MEDIUM, [3, 7], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [4, 6], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.FINAL, [5, 7], "_3", or_requirements=True)
|
||||
]
|
||||
|
||||
blitz_order = [
|
||||
FillMission("no_build", [-1], "I"),
|
||||
FillMission("easy", [-1], "I"),
|
||||
FillMission("medium", [0, 1], "II", number=1, or_requirements=True),
|
||||
FillMission("medium", [0, 1], "II", number=1, or_requirements=True),
|
||||
FillMission("medium", [0, 1], "III", number=2, or_requirements=True),
|
||||
FillMission("medium", [0, 1], "III", number=2, or_requirements=True),
|
||||
FillMission("hard", [0, 1], "IV", number=3, or_requirements=True),
|
||||
FillMission("hard", [0, 1], "IV", number=3, or_requirements=True),
|
||||
FillMission("hard", [0, 1], "V", number=4, or_requirements=True),
|
||||
FillMission("hard", [0, 1], "V", number=4, or_requirements=True),
|
||||
FillMission("hard", [0, 1], "Final", number=5, or_requirements=True),
|
||||
FillMission("all_in", [0, 1], "Final", number=5, or_requirements=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "I"),
|
||||
FillMission(MissionPools.EASY, [-1], "I"),
|
||||
FillMission(MissionPools.MEDIUM, [0, 1], "II", number=1, or_requirements=True),
|
||||
FillMission(MissionPools.MEDIUM, [0, 1], "II", number=1, or_requirements=True),
|
||||
FillMission(MissionPools.MEDIUM, [0, 1], "III", number=2, or_requirements=True),
|
||||
FillMission(MissionPools.MEDIUM, [0, 1], "III", number=2, or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [0, 1], "IV", number=3, or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [0, 1], "IV", number=3, or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [0, 1], "V", number=4, or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [0, 1], "V", number=4, or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [0, 1], "Final", number=5, or_requirements=True),
|
||||
FillMission(MissionPools.FINAL, [0, 1], "Final", number=5, or_requirements=True)
|
||||
]
|
||||
|
||||
mission_orders = [vanilla_shuffle_order, vanilla_shuffle_order, mini_campaign_order, grid_order, mini_grid_order, blitz_order, gauntlet_order]
|
||||
|
@ -176,40 +182,21 @@ vanilla_mission_req_table = {
|
|||
lookup_id_to_mission: Dict[int, str] = {
|
||||
data.id: mission_name for mission_name, data in vanilla_mission_req_table.items() if data.id}
|
||||
|
||||
no_build_starting_mission_locations = {
|
||||
starting_mission_locations = {
|
||||
"Liberation Day": "Liberation Day: Victory",
|
||||
"Breakout": "Breakout: Victory",
|
||||
"Ghost of a Chance": "Ghost of a Chance: Victory",
|
||||
"Piercing the Shroud": "Piercing the Shroud: Victory",
|
||||
"Whispers of Doom": "Whispers of Doom: Victory",
|
||||
"Belly of the Beast": "Belly of the Beast: Victory",
|
||||
}
|
||||
|
||||
build_starting_mission_locations = {
|
||||
"Zero Hour": "Zero Hour: First Group Rescued",
|
||||
"Evacuation": "Evacuation: First Chysalis",
|
||||
"Devil's Playground": "Devil's Playground: Tosh's Miners"
|
||||
}
|
||||
|
||||
advanced_starting_mission_locations = {
|
||||
"Devil's Playground": "Devil's Playground: Tosh's Miners",
|
||||
"Smash and Grab": "Smash and Grab: First Relic",
|
||||
"The Great Train Robbery": "The Great Train Robbery: North Defiler"
|
||||
}
|
||||
|
||||
|
||||
def get_starting_mission_locations(multiworld: MultiWorld, player: int) -> Set[str]:
|
||||
if get_option_value(multiworld, player, 'shuffle_no_build') or get_option_value(multiworld, player, 'mission_order') < 2:
|
||||
# Always start with a no-build mission unless explicitly relegating them
|
||||
# Vanilla and Vanilla Shuffled always start with a no-build even when relegated
|
||||
return no_build_starting_mission_locations
|
||||
elif get_option_value(multiworld, player, 'required_tactics') > 0:
|
||||
# Advanced Tactics/No Logic add more starting missions to the pool
|
||||
return {**build_starting_mission_locations, **advanced_starting_mission_locations}
|
||||
else:
|
||||
# Standard starting missions when relegate is on
|
||||
return build_starting_mission_locations
|
||||
|
||||
|
||||
alt_final_mission_locations = {
|
||||
"Maw of the Void": "Maw of the Void: Victory",
|
||||
"Engine of Destruction": "Engine of Destruction: Victory",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Dict
|
||||
from typing import Dict, FrozenSet, Union
|
||||
from BaseClasses import MultiWorld
|
||||
from Options import Choice, Option, Toggle, DefaultOnToggle, ItemSet, OptionSet, Range
|
||||
from .MissionTables import vanilla_mission_req_table
|
||||
|
||||
|
||||
class GameDifficulty(Choice):
|
||||
|
@ -110,6 +111,7 @@ class ExcludedMissions(OptionSet):
|
|||
Only applies on shortened mission orders.
|
||||
It may be impossible to build a valid campaign if too many missions are excluded."""
|
||||
display_name = "Excluded Missions"
|
||||
valid_keys = {mission_name for mission_name in vanilla_mission_req_table.keys() if mission_name != 'All-In'}
|
||||
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
|
@ -130,19 +132,10 @@ sc2wol_options: Dict[str, Option] = {
|
|||
}
|
||||
|
||||
|
||||
def get_option_value(multiworld: MultiWorld, player: int, name: str) -> int:
|
||||
option = getattr(multiworld, name, None)
|
||||
def get_option_value(multiworld: MultiWorld, player: int, name: str) -> Union[int, FrozenSet]:
|
||||
if multiworld is None:
|
||||
return sc2wol_options[name].default
|
||||
|
||||
if option is None:
|
||||
return 0
|
||||
player_option = getattr(multiworld, name)[player]
|
||||
|
||||
return int(option[player].value)
|
||||
|
||||
|
||||
def get_option_set_value(multiworld: MultiWorld, player: int, name: str) -> set:
|
||||
option = getattr(multiworld, name, None)
|
||||
|
||||
if option is None:
|
||||
return set()
|
||||
|
||||
return option[player].value
|
||||
return player_option.value
|
||||
|
|
|
@ -2,8 +2,8 @@ from typing import Callable, Dict, List, Set
|
|||
from BaseClasses import MultiWorld, ItemClassification, Item, Location
|
||||
from .Items import item_table
|
||||
from .MissionTables import no_build_regions_list, easy_regions_list, medium_regions_list, hard_regions_list,\
|
||||
mission_orders, get_starting_mission_locations, MissionInfo, vanilla_mission_req_table, alt_final_mission_locations
|
||||
from .Options import get_option_value, get_option_set_value
|
||||
mission_orders, MissionInfo, alt_final_mission_locations, MissionPools
|
||||
from .Options import get_option_value
|
||||
from .LogicMixin import SC2WoLLogic
|
||||
|
||||
# Items with associated upgrades
|
||||
|
@ -21,34 +21,33 @@ STARPORT_UNITS = {"Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", "He
|
|||
PROTOSS_REGIONS = {"A Sinister Turn", "Echoes of the Future", "In Utter Darkness"}
|
||||
|
||||
|
||||
def filter_missions(multiworld: MultiWorld, player: int) -> Dict[str, List[str]]:
|
||||
def filter_missions(multiworld: MultiWorld, player: int) -> Dict[int, List[str]]:
|
||||
"""
|
||||
Returns a semi-randomly pruned tuple of no-build, easy, medium, and hard mission sets
|
||||
"""
|
||||
|
||||
mission_order_type = get_option_value(multiworld, player, "mission_order")
|
||||
shuffle_no_build = get_option_value(multiworld, player, "shuffle_no_build")
|
||||
shuffle_protoss = get_option_value(multiworld, player, "shuffle_protoss")
|
||||
excluded_missions = set(get_option_set_value(multiworld, player, "excluded_missions"))
|
||||
invalid_mission_names = excluded_missions.difference(vanilla_mission_req_table.keys())
|
||||
if invalid_mission_names:
|
||||
raise Exception("Error in locked_missions - the following are not valid mission names: " + ", ".join(invalid_mission_names))
|
||||
excluded_missions = get_option_value(multiworld, player, "excluded_missions")
|
||||
mission_count = len(mission_orders[mission_order_type]) - 1
|
||||
# Vanilla and Vanilla Shuffled use the entire mission pool
|
||||
if mission_count == 28:
|
||||
return {
|
||||
"no_build": no_build_regions_list[:],
|
||||
"easy": easy_regions_list[:],
|
||||
"medium": medium_regions_list[:],
|
||||
"hard": hard_regions_list[:],
|
||||
"all_in": ["All-In"]
|
||||
}
|
||||
|
||||
mission_pools = [
|
||||
[],
|
||||
easy_regions_list,
|
||||
medium_regions_list,
|
||||
hard_regions_list
|
||||
]
|
||||
mission_pools = {
|
||||
MissionPools.STARTER: no_build_regions_list[:],
|
||||
MissionPools.EASY: easy_regions_list[:],
|
||||
MissionPools.MEDIUM: medium_regions_list[:],
|
||||
MissionPools.HARD: hard_regions_list[:],
|
||||
MissionPools.FINAL: []
|
||||
}
|
||||
if mission_order_type == 0:
|
||||
# Vanilla uses the entire mission pool
|
||||
mission_pools[MissionPools.FINAL] = ['All-In']
|
||||
return mission_pools
|
||||
elif mission_order_type == 1:
|
||||
# Vanilla Shuffled ignores the player-provided excluded missions
|
||||
excluded_missions = set()
|
||||
# Omitting No-Build missions if not shuffling no-build
|
||||
if not shuffle_no_build:
|
||||
excluded_missions = excluded_missions.union(no_build_regions_list)
|
||||
# Omitting Protoss missions if not shuffling protoss
|
||||
if not shuffle_protoss:
|
||||
excluded_missions = excluded_missions.union(PROTOSS_REGIONS)
|
||||
|
@ -58,46 +57,35 @@ def filter_missions(multiworld: MultiWorld, player: int) -> Dict[str, List[str]]
|
|||
excluded_missions.add(final_mission)
|
||||
else:
|
||||
final_mission = 'All-In'
|
||||
# Yaml settings determine which missions can be placed in the first slot
|
||||
mission_pools[0] = [mission for mission in get_starting_mission_locations(multiworld, player).keys() if mission not in excluded_missions]
|
||||
# Removing the new no-build missions from their original sets
|
||||
for i in range(1, len(mission_pools)):
|
||||
mission_pools[i] = [mission for mission in mission_pools[i] if mission not in excluded_missions.union(mission_pools[0])]
|
||||
# If the first mission is a build mission, there may not be enough locations to reach Outbreak as a second mission
|
||||
# Excluding missions
|
||||
for difficulty, mission_pool in mission_pools.items():
|
||||
mission_pools[difficulty] = [mission for mission in mission_pool if mission not in excluded_missions]
|
||||
mission_pools[MissionPools.FINAL].append(final_mission)
|
||||
# Mission pool changes on Build-Only
|
||||
if not get_option_value(multiworld, player, 'shuffle_no_build'):
|
||||
# Swapping Outbreak and The Great Train Robbery
|
||||
if "Outbreak" in mission_pools[1]:
|
||||
mission_pools[1].remove("Outbreak")
|
||||
mission_pools[2].append("Outbreak")
|
||||
if "The Great Train Robbery" in mission_pools[2]:
|
||||
mission_pools[2].remove("The Great Train Robbery")
|
||||
mission_pools[1].append("The Great Train Robbery")
|
||||
# Removing random missions from each difficulty set in a cycle
|
||||
set_cycle = 0
|
||||
current_count = sum(len(mission_pool) for mission_pool in mission_pools)
|
||||
def move_mission(mission_name, current_pool, new_pool):
|
||||
if mission_name in mission_pools[current_pool]:
|
||||
mission_pools[current_pool].remove(mission_name)
|
||||
mission_pools[new_pool].append(mission_name)
|
||||
# Replacing No Build missions with Easy missions
|
||||
move_mission("Zero Hour", MissionPools.EASY, MissionPools.STARTER)
|
||||
move_mission("Evacuation", MissionPools.EASY, MissionPools.STARTER)
|
||||
move_mission("Devil's Playground", MissionPools.EASY, MissionPools.STARTER)
|
||||
# Pushing Outbreak to Normal, as it cannot be placed as the second mission on Build-Only
|
||||
move_mission("Outbreak", MissionPools.EASY, MissionPools.MEDIUM)
|
||||
# Pushing extra Normal missions to Easy
|
||||
move_mission("The Great Train Robbery", MissionPools.MEDIUM, MissionPools.EASY)
|
||||
move_mission("Echoes of the Future", MissionPools.MEDIUM, MissionPools.EASY)
|
||||
move_mission("Cutthroat", MissionPools.MEDIUM, MissionPools.EASY)
|
||||
# Additional changes on Advanced Tactics
|
||||
if get_option_value(multiworld, player, "required_tactics") > 0:
|
||||
move_mission("The Great Train Robbery", MissionPools.EASY, MissionPools.STARTER)
|
||||
move_mission("Smash and Grab", MissionPools.EASY, MissionPools.STARTER)
|
||||
move_mission("Moebius Factor", MissionPools.MEDIUM, MissionPools.EASY)
|
||||
move_mission("Welcome to the Jungle", MissionPools.MEDIUM, MissionPools.EASY)
|
||||
move_mission("Engine of Destruction", MissionPools.HARD, MissionPools.MEDIUM)
|
||||
|
||||
if current_count < mission_count:
|
||||
raise Exception("Not enough missions available to fill the campaign on current settings. Please exclude fewer missions.")
|
||||
while current_count > mission_count:
|
||||
if set_cycle == 4:
|
||||
set_cycle = 0
|
||||
# Must contain at least one mission per set
|
||||
mission_pool = mission_pools[set_cycle]
|
||||
if len(mission_pool) <= 1:
|
||||
if all(len(mission_pool) <= 1 for mission_pool in mission_pools):
|
||||
raise Exception("Not enough missions available to fill the campaign on current settings. Please exclude fewer missions.")
|
||||
else:
|
||||
mission_pool.remove(multiworld.random.choice(mission_pool))
|
||||
current_count -= 1
|
||||
set_cycle += 1
|
||||
|
||||
return {
|
||||
"no_build": mission_pools[0],
|
||||
"easy": mission_pools[1],
|
||||
"medium": mission_pools[2],
|
||||
"hard": mission_pools[3],
|
||||
"all_in": [final_mission]
|
||||
}
|
||||
return mission_pools
|
||||
|
||||
|
||||
def get_item_upgrades(inventory: List[Item], parent_item: Item or str):
|
||||
|
@ -135,7 +123,21 @@ class ValidInventory:
|
|||
requirements = mission_requirements
|
||||
cascade_keys = self.cascade_removal_map.keys()
|
||||
units_always_have_upgrades = get_option_value(self.multiworld, self.player, "units_always_have_upgrades")
|
||||
if self.min_units_per_structure > 0:
|
||||
|
||||
# Locking associated items for items that have already been placed when units_always_have_upgrades is on
|
||||
if units_always_have_upgrades:
|
||||
existing_items = self.existing_items[:]
|
||||
while existing_items:
|
||||
existing_item = existing_items.pop()
|
||||
items_to_lock = self.cascade_removal_map.get(existing_item, [existing_item])
|
||||
for item in items_to_lock:
|
||||
if item in inventory:
|
||||
inventory.remove(item)
|
||||
locked_items.append(item)
|
||||
if item in existing_items:
|
||||
existing_items.remove(item)
|
||||
|
||||
if self.min_units_per_structure > 0 and self.has_units_per_structure():
|
||||
requirements.append(lambda state: state.has_units_per_structure())
|
||||
|
||||
def attempt_removal(item: Item) -> bool:
|
||||
|
@ -151,6 +153,10 @@ class ValidInventory:
|
|||
return False
|
||||
return True
|
||||
|
||||
# Determining if the full-size inventory can complete campaign
|
||||
if not all(requirement(self) for requirement in requirements):
|
||||
raise Exception("Too many items excluded - campaign is impossible to complete.")
|
||||
|
||||
while len(inventory) + len(locked_items) > inventory_size:
|
||||
if len(inventory) == 0:
|
||||
raise Exception("Reduced item pool generation failed - not enough locations available to place items.")
|
||||
|
|
|
@ -2,7 +2,7 @@ from typing import List, Set, Dict, Tuple, Optional, Callable
|
|||
from BaseClasses import MultiWorld, Region, Entrance, Location
|
||||
from .Locations import LocationData
|
||||
from .Options import get_option_value
|
||||
from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, alt_final_mission_locations
|
||||
from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, alt_final_mission_locations, MissionPools
|
||||
from .PoolFilter import filter_missions
|
||||
|
||||
|
||||
|
@ -14,34 +14,18 @@ def create_regions(multiworld: MultiWorld, player: int, locations: Tuple[Locatio
|
|||
mission_order = mission_orders[mission_order_type]
|
||||
|
||||
mission_pools = filter_missions(multiworld, player)
|
||||
final_mission = mission_pools['all_in'][0]
|
||||
|
||||
used_regions = [mission for mission_pool in mission_pools.values() for mission in mission_pool]
|
||||
regions = [create_region(multiworld, player, locations_per_region, location_cache, "Menu")]
|
||||
for region_name in used_regions:
|
||||
regions.append(create_region(multiworld, player, locations_per_region, location_cache, region_name))
|
||||
# Changing the completion condition for alternate final missions into an event
|
||||
if final_mission != 'All-In':
|
||||
final_location = alt_final_mission_locations[final_mission]
|
||||
# Final location should be near the end of the cache
|
||||
for i in range(len(location_cache) - 1, -1, -1):
|
||||
if location_cache[i].name == final_location:
|
||||
location_cache[i].locked = True
|
||||
location_cache[i].event = True
|
||||
location_cache[i].address = None
|
||||
break
|
||||
else:
|
||||
final_location = 'All-In: Victory'
|
||||
|
||||
if __debug__:
|
||||
if mission_order_type in (0, 1):
|
||||
throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys())
|
||||
|
||||
multiworld.regions += regions
|
||||
|
||||
names: Dict[str, int] = {}
|
||||
|
||||
if mission_order_type == 0:
|
||||
|
||||
# Generating all regions and locations
|
||||
for region_name in vanilla_mission_req_table.keys():
|
||||
regions.append(create_region(multiworld, player, locations_per_region, location_cache, region_name))
|
||||
multiworld.regions += regions
|
||||
|
||||
connect(multiworld, player, names, 'Menu', 'Liberation Day'),
|
||||
connect(multiworld, player, names, 'Liberation Day', 'The Outlaws',
|
||||
lambda state: state.has("Beat Liberation Day", player)),
|
||||
|
@ -110,31 +94,32 @@ def create_regions(multiworld: MultiWorld, player: int, locations: Tuple[Locatio
|
|||
lambda state: state.has('Beat Gates of Hell', player) and (
|
||||
state.has('Beat Shatter the Sky', player) or state.has('Beat Belly of the Beast', player)))
|
||||
|
||||
return vanilla_mission_req_table, 29, final_location
|
||||
return vanilla_mission_req_table, 29, 'All-In: Victory'
|
||||
|
||||
else:
|
||||
missions = []
|
||||
|
||||
remove_prophecy = mission_order_type == 1 and not get_option_value(multiworld, player, "shuffle_protoss")
|
||||
|
||||
final_mission = mission_pools[MissionPools.FINAL][0]
|
||||
|
||||
# Determining if missions must be removed
|
||||
mission_pool_size = sum(len(mission_pool) for mission_pool in mission_pools.values())
|
||||
removals = len(mission_order) - mission_pool_size
|
||||
# Removing entire Prophecy chain on vanilla shuffled when not shuffling protoss
|
||||
if remove_prophecy:
|
||||
removals -= 4
|
||||
|
||||
# Initial fill out of mission list and marking all-in mission
|
||||
for mission in mission_order:
|
||||
if mission is None:
|
||||
# Removing extra missions if mission pool is too small
|
||||
if 0 < mission.removal_priority <= removals or mission.category == 'Prophecy' and remove_prophecy:
|
||||
missions.append(None)
|
||||
elif mission.type == "all_in":
|
||||
elif mission.type == MissionPools.FINAL:
|
||||
missions.append(final_mission)
|
||||
elif mission.relegate and not get_option_value(multiworld, player, "shuffle_no_build"):
|
||||
missions.append("no_build")
|
||||
else:
|
||||
missions.append(mission.type)
|
||||
|
||||
# Place Protoss Missions if we are not using ShuffleProtoss and are in Vanilla Shuffled
|
||||
if get_option_value(multiworld, player, "shuffle_protoss") == 0 and mission_order_type == 1:
|
||||
missions[22] = "A Sinister Turn"
|
||||
mission_pools['medium'].remove("A Sinister Turn")
|
||||
missions[23] = "Echoes of the Future"
|
||||
mission_pools['medium'].remove("Echoes of the Future")
|
||||
missions[24] = "In Utter Darkness"
|
||||
mission_pools['hard'].remove("In Utter Darkness")
|
||||
|
||||
no_build_slots = []
|
||||
easy_slots = []
|
||||
medium_slots = []
|
||||
|
@ -144,79 +129,108 @@ def create_regions(multiworld: MultiWorld, player: int, locations: Tuple[Locatio
|
|||
for i in range(len(missions)):
|
||||
if missions[i] is None:
|
||||
continue
|
||||
if missions[i] == "no_build":
|
||||
if missions[i] == MissionPools.STARTER:
|
||||
no_build_slots.append(i)
|
||||
elif missions[i] == "easy":
|
||||
elif missions[i] == MissionPools.EASY:
|
||||
easy_slots.append(i)
|
||||
elif missions[i] == "medium":
|
||||
elif missions[i] == MissionPools.MEDIUM:
|
||||
medium_slots.append(i)
|
||||
elif missions[i] == "hard":
|
||||
elif missions[i] == MissionPools.HARD:
|
||||
hard_slots.append(i)
|
||||
|
||||
# Add no_build missions to the pool and fill in no_build slots
|
||||
missions_to_add = mission_pools['no_build']
|
||||
missions_to_add = mission_pools[MissionPools.STARTER]
|
||||
if len(no_build_slots) > len(missions_to_add):
|
||||
raise Exception("There are no valid No-Build missions. Please exclude fewer missions.")
|
||||
for slot in no_build_slots:
|
||||
filler = multiworld.random.randint(0, len(missions_to_add) - 1)
|
||||
|
||||
missions[slot] = missions_to_add.pop(filler)
|
||||
|
||||
# Add easy missions into pool and fill in easy slots
|
||||
missions_to_add = missions_to_add + mission_pools['easy']
|
||||
missions_to_add = missions_to_add + mission_pools[MissionPools.EASY]
|
||||
if len(easy_slots) > len(missions_to_add):
|
||||
raise Exception("There are not enough Easy missions to fill the campaign. Please exclude fewer missions.")
|
||||
for slot in easy_slots:
|
||||
filler = multiworld.random.randint(0, len(missions_to_add) - 1)
|
||||
|
||||
missions[slot] = missions_to_add.pop(filler)
|
||||
|
||||
# Add medium missions into pool and fill in medium slots
|
||||
missions_to_add = missions_to_add + mission_pools['medium']
|
||||
missions_to_add = missions_to_add + mission_pools[MissionPools.MEDIUM]
|
||||
if len(medium_slots) > len(missions_to_add):
|
||||
raise Exception("There are not enough Easy and Medium missions to fill the campaign. Please exclude fewer missions.")
|
||||
for slot in medium_slots:
|
||||
filler = multiworld.random.randint(0, len(missions_to_add) - 1)
|
||||
|
||||
missions[slot] = missions_to_add.pop(filler)
|
||||
|
||||
# Add hard missions into pool and fill in hard slots
|
||||
missions_to_add = missions_to_add + mission_pools['hard']
|
||||
missions_to_add = missions_to_add + mission_pools[MissionPools.HARD]
|
||||
if len(hard_slots) > len(missions_to_add):
|
||||
raise Exception("There are not enough missions to fill the campaign. Please exclude fewer missions.")
|
||||
for slot in hard_slots:
|
||||
filler = multiworld.random.randint(0, len(missions_to_add) - 1)
|
||||
|
||||
missions[slot] = missions_to_add.pop(filler)
|
||||
|
||||
# Generating regions and locations from selected missions
|
||||
for region_name in missions:
|
||||
regions.append(create_region(multiworld, player, locations_per_region, location_cache, region_name))
|
||||
multiworld.regions += regions
|
||||
|
||||
# Mapping original mission slots to shifted mission slots when missions are removed
|
||||
slot_map = []
|
||||
slot_offset = 0
|
||||
for position, mission in enumerate(missions):
|
||||
slot_map.append(position - slot_offset + 1)
|
||||
if mission is None:
|
||||
slot_offset += 1
|
||||
|
||||
# Loop through missions to create requirements table and connect regions
|
||||
# TODO: Handle 'and' connections
|
||||
mission_req_table = {}
|
||||
for i in range(len(missions)):
|
||||
|
||||
for i, mission in enumerate(missions):
|
||||
if mission is None:
|
||||
continue
|
||||
connections = []
|
||||
for connection in mission_order[i].connect_to:
|
||||
required_mission = missions[connection]
|
||||
if connection == -1:
|
||||
connect(multiworld, player, names, "Menu", missions[i])
|
||||
connect(multiworld, player, names, "Menu", mission)
|
||||
elif required_mission is None:
|
||||
continue
|
||||
else:
|
||||
connect(multiworld, player, names, missions[connection], missions[i],
|
||||
connect(multiworld, player, names, required_mission, mission,
|
||||
(lambda name, missions_req: (lambda state: state.has(f"Beat {name}", player) and
|
||||
state._sc2wol_cleared_missions(multiworld, player,
|
||||
missions_req)))
|
||||
(missions[connection], mission_order[i].number))
|
||||
connections.append(connection + 1)
|
||||
connections.append(slot_map[connection])
|
||||
|
||||
mission_req_table.update({missions[i]: MissionInfo(
|
||||
vanilla_mission_req_table[missions[i]].id, connections, mission_order[i].category,
|
||||
mission_req_table.update({mission: MissionInfo(
|
||||
vanilla_mission_req_table[mission].id, connections, mission_order[i].category,
|
||||
number=mission_order[i].number,
|
||||
completion_critical=mission_order[i].completion_critical,
|
||||
or_requirements=mission_order[i].or_requirements)})
|
||||
|
||||
final_mission_id = vanilla_mission_req_table[final_mission].id
|
||||
return mission_req_table, final_mission_id, final_mission + ': Victory'
|
||||
|
||||
# Changing the completion condition for alternate final missions into an event
|
||||
if final_mission != 'All-In':
|
||||
final_location = alt_final_mission_locations[final_mission]
|
||||
# Final location should be near the end of the cache
|
||||
for i in range(len(location_cache) - 1, -1, -1):
|
||||
if location_cache[i].name == final_location:
|
||||
location_cache[i].locked = True
|
||||
location_cache[i].event = True
|
||||
location_cache[i].address = None
|
||||
break
|
||||
else:
|
||||
final_location = 'All-In: Victory'
|
||||
|
||||
def throwIfAnyLocationIsNotAssignedToARegion(regions: List[Region], regionNames: Set[str]):
|
||||
existingRegions = set()
|
||||
|
||||
for region in regions:
|
||||
existingRegions.add(region.name)
|
||||
|
||||
if (regionNames - existingRegions):
|
||||
raise Exception("Starcraft: the following regions are used in locations: {}, but no such region exists".format(
|
||||
regionNames - existingRegions))
|
||||
|
||||
return mission_req_table, final_mission_id, final_location
|
||||
|
||||
def create_location(player: int, location_data: LocationData, region: Region,
|
||||
location_cache: List[Location]) -> Location:
|
||||
|
|
|
@ -7,10 +7,10 @@ from .Items import StarcraftWoLItem, item_table, filler_items, item_name_groups,
|
|||
get_basic_units
|
||||
from .Locations import get_locations
|
||||
from .Regions import create_regions
|
||||
from .Options import sc2wol_options, get_option_value, get_option_set_value
|
||||
from .Options import sc2wol_options, get_option_value
|
||||
from .LogicMixin import SC2WoLLogic
|
||||
from .PoolFilter import filter_missions, filter_items, get_item_upgrades
|
||||
from .MissionTables import get_starting_mission_locations, MissionInfo
|
||||
from .MissionTables import starting_mission_locations, MissionInfo
|
||||
|
||||
|
||||
class Starcraft2WoLWebWorld(WebWorld):
|
||||
|
@ -137,7 +137,6 @@ def assign_starter_items(multiworld: MultiWorld, player: int, excluded_items: Se
|
|||
|
||||
# The first world should also be the starting world
|
||||
first_mission = list(multiworld.worlds[player].mission_req_table)[0]
|
||||
starting_mission_locations = get_starting_mission_locations(multiworld, player)
|
||||
if first_mission in starting_mission_locations:
|
||||
first_location = starting_mission_locations[first_mission]
|
||||
elif first_mission == "In Utter Darkness":
|
||||
|
@ -174,7 +173,7 @@ def get_item_pool(multiworld: MultiWorld, player: int, mission_req_table: Dict[s
|
|||
locked_items = []
|
||||
|
||||
# YAML items
|
||||
yaml_locked_items = get_option_set_value(multiworld, player, 'locked_items')
|
||||
yaml_locked_items = get_option_value(multiworld, player, 'locked_items')
|
||||
|
||||
for name, data in item_table.items():
|
||||
if name not in excluded_items:
|
||||
|
|
Loading…
Reference in New Issue