SC2: Various bugfixes (#1267)

* SC2: Fixed nondeterminism resulting from early unit placement

* SC2: Renamed "world" arguments to "multiworld"

* SC2: Fixed All-In Ground including anti-air in defense score, fixed error in beats_protoss_deathball

* SC2: Fixed No Logic using logic on Beat events

* SC2: Fixed /unfinished command failing when All-In available
This commit is contained in:
Magnemania 2022-12-11 14:46:24 -05:00 committed by GitHub
parent e3f169b4c3
commit e71ea94fe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 290 additions and 282 deletions

View File

@ -639,6 +639,13 @@ def request_unfinished_missions(ctx: SC2Context):
_, unfinished_missions = calc_unfinished_missions(ctx, unlocks=unlocks) _, unfinished_missions = calc_unfinished_missions(ctx, unlocks=unlocks)
# Removing All-In from location pool
final_mission = lookup_id_to_mission[ctx.final_mission]
if final_mission in unfinished_missions.keys():
message = f"Final Mission Available: {final_mission}[{ctx.final_mission}]\n" + message
if unfinished_missions[final_mission] == -1:
unfinished_missions.pop(final_mission)
message += ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}[{ctx.mission_req_table[mission].id}] " + message += ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}[{ctx.mission_req_table[mission].id}] " +
mark_up_objectives( mark_up_objectives(
f"[{len(unfinished_missions[mission])}/" f"[{len(unfinished_missions[mission])}/"

View File

@ -157,17 +157,17 @@ basic_units = {
'Vulture' 'Vulture'
} }
advanced_basic_units = { advanced_basic_units = basic_units.union({
'Reaper', 'Reaper',
'Goliath', 'Goliath',
'Diamondback', 'Diamondback',
'Viking' 'Viking'
} })
def get_basic_units(world: MultiWorld, player: int) -> typing.Set[str]: def get_basic_units(multiworld: MultiWorld, player: int) -> typing.Set[str]:
if get_option_value(world, player, 'required_tactics') > 0: if get_option_value(multiworld, player, 'required_tactics') > 0:
return basic_units.union(advanced_basic_units) return advanced_basic_units
else: else:
return basic_units return basic_units
@ -193,7 +193,7 @@ defense_ratings = {
} }
zerg_defense_ratings = { zerg_defense_ratings = {
"Perdition Turret": 2, "Perdition Turret": 2,
# Bunker w/ Firebat # Bunker w/ Firebat: 2
"Hive Mind Emulator": 3, "Hive Mind Emulator": 3,
"Psi Disruptor": 3 "Psi Disruptor": 3
} }

View File

@ -18,9 +18,9 @@ class LocationData(NamedTuple):
rule: Callable = lambda state: True rule: Callable = lambda state: True
def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[LocationData, ...]: def get_locations(multiworld: Optional[MultiWorld], player: Optional[int]) -> Tuple[LocationData, ...]:
# Note: rules which are ended with or True are rules identified as needed later when restricted units is an option # Note: rules which are ended with or True are rules identified as needed later when restricted units is an option
logic_level = get_option_value(world, player, 'required_tactics') logic_level = get_option_value(multiworld, player, 'required_tactics')
location_table: List[LocationData] = [ location_table: List[LocationData] = [
LocationData("Liberation Day", "Liberation Day: Victory", SC2WOL_LOC_ID_OFFSET + 100), LocationData("Liberation Day", "Liberation Day: Victory", SC2WOL_LOC_ID_OFFSET + 100),
LocationData("Liberation Day", "Liberation Day: First Statue", SC2WOL_LOC_ID_OFFSET + 101), LocationData("Liberation Day", "Liberation Day: First Statue", SC2WOL_LOC_ID_OFFSET + 101),
@ -30,144 +30,144 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
LocationData("Liberation Day", "Liberation Day: Fifth Statue", SC2WOL_LOC_ID_OFFSET + 105), LocationData("Liberation Day", "Liberation Day: Fifth Statue", SC2WOL_LOC_ID_OFFSET + 105),
LocationData("Liberation Day", "Liberation Day: Sixth Statue", SC2WOL_LOC_ID_OFFSET + 106), LocationData("Liberation Day", "Liberation Day: Sixth Statue", SC2WOL_LOC_ID_OFFSET + 106),
LocationData("The Outlaws", "The Outlaws: Victory", SC2WOL_LOC_ID_OFFSET + 200, LocationData("The Outlaws", "The Outlaws: Victory", SC2WOL_LOC_ID_OFFSET + 200,
lambda state: state._sc2wol_has_common_unit(world, player)), lambda state: state._sc2wol_has_common_unit(multiworld, player)),
LocationData("The Outlaws", "The Outlaws: Rebel Base", SC2WOL_LOC_ID_OFFSET + 201, LocationData("The Outlaws", "The Outlaws: Rebel Base", SC2WOL_LOC_ID_OFFSET + 201,
lambda state: state._sc2wol_has_common_unit(world, player)), lambda state: state._sc2wol_has_common_unit(multiworld, player)),
LocationData("Zero Hour", "Zero Hour: Victory", SC2WOL_LOC_ID_OFFSET + 300, LocationData("Zero Hour", "Zero Hour: Victory", SC2WOL_LOC_ID_OFFSET + 300,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_defense_rating(world, player, True) >= 2 and state._sc2wol_defense_rating(multiworld, player, True) >= 2 and
(logic_level > 0 or state._sc2wol_has_anti_air(world, player))), (logic_level > 0 or state._sc2wol_has_anti_air(multiworld, player))),
LocationData("Zero Hour", "Zero Hour: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 301), LocationData("Zero Hour", "Zero Hour: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 301),
LocationData("Zero Hour", "Zero Hour: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 302, LocationData("Zero Hour", "Zero Hour: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 302,
lambda state: state._sc2wol_has_common_unit(world, player)), lambda state: state._sc2wol_has_common_unit(multiworld, player)),
LocationData("Zero Hour", "Zero Hour: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 303, LocationData("Zero Hour", "Zero Hour: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 303,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_defense_rating(world, player, True) >= 2), state._sc2wol_defense_rating(multiworld, player, True) >= 2),
LocationData("Evacuation", "Evacuation: Victory", SC2WOL_LOC_ID_OFFSET + 400, LocationData("Evacuation", "Evacuation: Victory", SC2WOL_LOC_ID_OFFSET + 400,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
(logic_level > 0 and state._sc2wol_has_anti_air(world, player) (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
or state._sc2wol_has_competent_anti_air(world, player))), or state._sc2wol_has_competent_anti_air(multiworld, player))),
LocationData("Evacuation", "Evacuation: First Chysalis", SC2WOL_LOC_ID_OFFSET + 401), LocationData("Evacuation", "Evacuation: First Chysalis", SC2WOL_LOC_ID_OFFSET + 401),
LocationData("Evacuation", "Evacuation: Second Chysalis", SC2WOL_LOC_ID_OFFSET + 402, LocationData("Evacuation", "Evacuation: Second Chysalis", SC2WOL_LOC_ID_OFFSET + 402,
lambda state: state._sc2wol_has_common_unit(world, player)), lambda state: state._sc2wol_has_common_unit(multiworld, player)),
LocationData("Evacuation", "Evacuation: Third Chysalis", SC2WOL_LOC_ID_OFFSET + 403, LocationData("Evacuation", "Evacuation: Third Chysalis", SC2WOL_LOC_ID_OFFSET + 403,
lambda state: state._sc2wol_has_common_unit(world, player)), lambda state: state._sc2wol_has_common_unit(multiworld, player)),
LocationData("Outbreak", "Outbreak: Victory", SC2WOL_LOC_ID_OFFSET + 500, LocationData("Outbreak", "Outbreak: Victory", SC2WOL_LOC_ID_OFFSET + 500,
lambda state: state._sc2wol_defense_rating(world, player, True, False) >= 4 and lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 4 and
(state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player))), (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
LocationData("Outbreak", "Outbreak: Left Infestor", SC2WOL_LOC_ID_OFFSET + 501, LocationData("Outbreak", "Outbreak: Left Infestor", SC2WOL_LOC_ID_OFFSET + 501,
lambda state: state._sc2wol_defense_rating(world, player, True, False) >= 2 and lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and
(state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player))), (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
LocationData("Outbreak", "Outbreak: Right Infestor", SC2WOL_LOC_ID_OFFSET + 502, LocationData("Outbreak", "Outbreak: Right Infestor", SC2WOL_LOC_ID_OFFSET + 502,
lambda state: state._sc2wol_defense_rating(world, player, True, False) >= 2 and lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and
(state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player))), (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
LocationData("Safe Haven", "Safe Haven: Victory", SC2WOL_LOC_ID_OFFSET + 600, LocationData("Safe Haven", "Safe Haven: Victory", SC2WOL_LOC_ID_OFFSET + 600,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player)), state._sc2wol_has_competent_anti_air(multiworld, player)),
LocationData("Safe Haven", "Safe Haven: North Nexus", SC2WOL_LOC_ID_OFFSET + 601, LocationData("Safe Haven", "Safe Haven: North Nexus", SC2WOL_LOC_ID_OFFSET + 601,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player)), state._sc2wol_has_competent_anti_air(multiworld, player)),
LocationData("Safe Haven", "Safe Haven: East Nexus", SC2WOL_LOC_ID_OFFSET + 602, LocationData("Safe Haven", "Safe Haven: East Nexus", SC2WOL_LOC_ID_OFFSET + 602,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player)), state._sc2wol_has_competent_anti_air(multiworld, player)),
LocationData("Safe Haven", "Safe Haven: South Nexus", SC2WOL_LOC_ID_OFFSET + 603, LocationData("Safe Haven", "Safe Haven: South Nexus", SC2WOL_LOC_ID_OFFSET + 603,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player)), state._sc2wol_has_competent_anti_air(multiworld, player)),
LocationData("Haven's Fall", "Haven's Fall: Victory", SC2WOL_LOC_ID_OFFSET + 700, LocationData("Haven's Fall", "Haven's Fall: Victory", SC2WOL_LOC_ID_OFFSET + 700,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player) and state._sc2wol_has_competent_anti_air(multiworld, player) and
state._sc2wol_defense_rating(world, player, True) >= 3), state._sc2wol_defense_rating(multiworld, player, True) >= 3),
LocationData("Haven's Fall", "Haven's Fall: North Hive", SC2WOL_LOC_ID_OFFSET + 701, LocationData("Haven's Fall", "Haven's Fall: North Hive", SC2WOL_LOC_ID_OFFSET + 701,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player) and state._sc2wol_has_competent_anti_air(multiworld, player) and
state._sc2wol_defense_rating(world, player, True) >= 3), state._sc2wol_defense_rating(multiworld, player, True) >= 3),
LocationData("Haven's Fall", "Haven's Fall: East Hive", SC2WOL_LOC_ID_OFFSET + 702, LocationData("Haven's Fall", "Haven's Fall: East Hive", SC2WOL_LOC_ID_OFFSET + 702,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player) and state._sc2wol_has_competent_anti_air(multiworld, player) and
state._sc2wol_defense_rating(world, player, True) >= 3), state._sc2wol_defense_rating(multiworld, player, True) >= 3),
LocationData("Haven's Fall", "Haven's Fall: South Hive", SC2WOL_LOC_ID_OFFSET + 703, LocationData("Haven's Fall", "Haven's Fall: South Hive", SC2WOL_LOC_ID_OFFSET + 703,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player) and state._sc2wol_has_competent_anti_air(multiworld, player) and
state._sc2wol_defense_rating(world, player, True) >= 3), state._sc2wol_defense_rating(multiworld, player, True) >= 3),
LocationData("Smash and Grab", "Smash and Grab: Victory", SC2WOL_LOC_ID_OFFSET + 800, LocationData("Smash and Grab", "Smash and Grab: Victory", SC2WOL_LOC_ID_OFFSET + 800,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
(logic_level > 0 and state._sc2wol_has_anti_air(world, player) (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
or state._sc2wol_has_competent_anti_air(world, player))), or state._sc2wol_has_competent_anti_air(multiworld, player))),
LocationData("Smash and Grab", "Smash and Grab: First Relic", SC2WOL_LOC_ID_OFFSET + 801), LocationData("Smash and Grab", "Smash and Grab: First Relic", SC2WOL_LOC_ID_OFFSET + 801),
LocationData("Smash and Grab", "Smash and Grab: Second Relic", SC2WOL_LOC_ID_OFFSET + 802), LocationData("Smash and Grab", "Smash and Grab: Second Relic", SC2WOL_LOC_ID_OFFSET + 802),
LocationData("Smash and Grab", "Smash and Grab: Third Relic", SC2WOL_LOC_ID_OFFSET + 803, LocationData("Smash and Grab", "Smash and Grab: Third Relic", SC2WOL_LOC_ID_OFFSET + 803,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
(logic_level > 0 and state._sc2wol_has_anti_air(world, player) (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
or state._sc2wol_has_competent_anti_air(world, player))), or state._sc2wol_has_competent_anti_air(multiworld, player))),
LocationData("Smash and Grab", "Smash and Grab: Fourth Relic", SC2WOL_LOC_ID_OFFSET + 804, LocationData("Smash and Grab", "Smash and Grab: Fourth Relic", SC2WOL_LOC_ID_OFFSET + 804,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
(logic_level > 0 and state._sc2wol_has_anti_air(world, player) (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
or state._sc2wol_has_competent_anti_air(world, player))), or state._sc2wol_has_competent_anti_air(multiworld, player))),
LocationData("The Dig", "The Dig: Victory", SC2WOL_LOC_ID_OFFSET + 900, LocationData("The Dig", "The Dig: Victory", SC2WOL_LOC_ID_OFFSET + 900,
lambda state: state._sc2wol_has_anti_air(world, player) and lambda state: state._sc2wol_has_anti_air(multiworld, player) and
state._sc2wol_defense_rating(world, player, False) >= 7), state._sc2wol_defense_rating(multiworld, player, False) >= 7),
LocationData("The Dig", "The Dig: Left Relic", SC2WOL_LOC_ID_OFFSET + 901, LocationData("The Dig", "The Dig: Left Relic", SC2WOL_LOC_ID_OFFSET + 901,
lambda state: state._sc2wol_defense_rating(world, player, False) >= 5), lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5),
LocationData("The Dig", "The Dig: Right Ground Relic", SC2WOL_LOC_ID_OFFSET + 902, LocationData("The Dig", "The Dig: Right Ground Relic", SC2WOL_LOC_ID_OFFSET + 902,
lambda state: state._sc2wol_defense_rating(world, player, False) >= 5), lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5),
LocationData("The Dig", "The Dig: Right Cliff Relic", SC2WOL_LOC_ID_OFFSET + 903, LocationData("The Dig", "The Dig: Right Cliff Relic", SC2WOL_LOC_ID_OFFSET + 903,
lambda state: state._sc2wol_defense_rating(world, player, False) >= 5), lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5),
LocationData("The Moebius Factor", "The Moebius Factor: Victory", SC2WOL_LOC_ID_OFFSET + 1000, LocationData("The Moebius Factor", "The Moebius Factor: Victory", SC2WOL_LOC_ID_OFFSET + 1000,
lambda state: state._sc2wol_has_anti_air(world, player) and lambda state: state._sc2wol_has_anti_air(multiworld, player) and
(state._sc2wol_has_air(world, player) (state._sc2wol_has_air(multiworld, player)
or state.has_any({'Medivac', 'Hercules'}, player) or state.has_any({'Medivac', 'Hercules'}, player)
and state._sc2wol_has_common_unit(world, player))), and state._sc2wol_has_common_unit(multiworld, player))),
LocationData("The Moebius Factor", "The Moebius Factor: South Rescue", SC2WOL_LOC_ID_OFFSET + 1003, LocationData("The Moebius Factor", "The Moebius Factor: South Rescue", SC2WOL_LOC_ID_OFFSET + 1003,
lambda state: state._sc2wol_able_to_rescue(world, player)), lambda state: state._sc2wol_able_to_rescue(multiworld, player)),
LocationData("The Moebius Factor", "The Moebius Factor: Wall Rescue", SC2WOL_LOC_ID_OFFSET + 1004, LocationData("The Moebius Factor", "The Moebius Factor: Wall Rescue", SC2WOL_LOC_ID_OFFSET + 1004,
lambda state: state._sc2wol_able_to_rescue(world, player)), lambda state: state._sc2wol_able_to_rescue(multiworld, player)),
LocationData("The Moebius Factor", "The Moebius Factor: Mid Rescue", SC2WOL_LOC_ID_OFFSET + 1005, LocationData("The Moebius Factor", "The Moebius Factor: Mid Rescue", SC2WOL_LOC_ID_OFFSET + 1005,
lambda state: state._sc2wol_able_to_rescue(world, player)), lambda state: state._sc2wol_able_to_rescue(multiworld, player)),
LocationData("The Moebius Factor", "The Moebius Factor: Nydus Roof Rescue", SC2WOL_LOC_ID_OFFSET + 1006, LocationData("The Moebius Factor", "The Moebius Factor: Nydus Roof Rescue", SC2WOL_LOC_ID_OFFSET + 1006,
lambda state: state._sc2wol_able_to_rescue(world, player)), lambda state: state._sc2wol_able_to_rescue(multiworld, player)),
LocationData("The Moebius Factor", "The Moebius Factor: Alive Inside Rescue", SC2WOL_LOC_ID_OFFSET + 1007, LocationData("The Moebius Factor", "The Moebius Factor: Alive Inside Rescue", SC2WOL_LOC_ID_OFFSET + 1007,
lambda state: state._sc2wol_able_to_rescue(world, player)), lambda state: state._sc2wol_able_to_rescue(multiworld, player)),
LocationData("The Moebius Factor", "The Moebius Factor: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1008, LocationData("The Moebius Factor", "The Moebius Factor: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1008,
lambda state: state._sc2wol_has_anti_air(world, player) and lambda state: state._sc2wol_has_anti_air(multiworld, player) and
(state._sc2wol_has_air(world, player) (state._sc2wol_has_air(multiworld, player)
or state.has_any({'Medivac', 'Hercules'}, player) or state.has_any({'Medivac', 'Hercules'}, player)
and state._sc2wol_has_common_unit(world, player))), and state._sc2wol_has_common_unit(multiworld, player))),
LocationData("Supernova", "Supernova: Victory", SC2WOL_LOC_ID_OFFSET + 1100, LocationData("Supernova", "Supernova: Victory", SC2WOL_LOC_ID_OFFSET + 1100,
lambda state: state._sc2wol_beats_protoss_deathball(world, player)), lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)),
LocationData("Supernova", "Supernova: West Relic", SC2WOL_LOC_ID_OFFSET + 1101), LocationData("Supernova", "Supernova: West Relic", SC2WOL_LOC_ID_OFFSET + 1101),
LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102), LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102),
LocationData("Supernova", "Supernova: South Relic", SC2WOL_LOC_ID_OFFSET + 1103, LocationData("Supernova", "Supernova: South Relic", SC2WOL_LOC_ID_OFFSET + 1103,
lambda state: state._sc2wol_beats_protoss_deathball(world, player)), lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)),
LocationData("Supernova", "Supernova: East Relic", SC2WOL_LOC_ID_OFFSET + 1104, LocationData("Supernova", "Supernova: East Relic", SC2WOL_LOC_ID_OFFSET + 1104,
lambda state: state._sc2wol_beats_protoss_deathball(world, player)), lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)),
LocationData("Maw of the Void", "Maw of the Void: Victory", SC2WOL_LOC_ID_OFFSET + 1200, LocationData("Maw of the Void", "Maw of the Void: Victory", SC2WOL_LOC_ID_OFFSET + 1200,
lambda state: state._sc2wol_survives_rip_field(world, player)), lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
LocationData("Maw of the Void", "Maw of the Void: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1201), LocationData("Maw of the Void", "Maw of the Void: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1201),
LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202, LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202,
lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(world, player)), lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(multiworld, player)),
LocationData("Maw of the Void", "Maw of the Void: South Close Prisoners", SC2WOL_LOC_ID_OFFSET + 1203, LocationData("Maw of the Void", "Maw of the Void: South Close Prisoners", SC2WOL_LOC_ID_OFFSET + 1203,
lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(world, player)), lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(multiworld, player)),
LocationData("Maw of the Void", "Maw of the Void: South Far Prisoners", SC2WOL_LOC_ID_OFFSET + 1204, LocationData("Maw of the Void", "Maw of the Void: South Far Prisoners", SC2WOL_LOC_ID_OFFSET + 1204,
lambda state: state._sc2wol_survives_rip_field(world, player)), lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
LocationData("Maw of the Void", "Maw of the Void: North Prisoners", SC2WOL_LOC_ID_OFFSET + 1205, LocationData("Maw of the Void", "Maw of the Void: North Prisoners", SC2WOL_LOC_ID_OFFSET + 1205,
lambda state: state._sc2wol_survives_rip_field(world, player)), lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
LocationData("Devil's Playground", "Devil's Playground: Victory", SC2WOL_LOC_ID_OFFSET + 1300, LocationData("Devil's Playground", "Devil's Playground: Victory", SC2WOL_LOC_ID_OFFSET + 1300,
lambda state: logic_level > 0 or lambda state: logic_level > 0 or
state._sc2wol_has_anti_air(world, player) and ( state._sc2wol_has_anti_air(multiworld, player) and (
state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player))), state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
LocationData("Devil's Playground", "Devil's Playground: Tosh's Miners", SC2WOL_LOC_ID_OFFSET + 1301), LocationData("Devil's Playground", "Devil's Playground: Tosh's Miners", SC2WOL_LOC_ID_OFFSET + 1301),
LocationData("Devil's Playground", "Devil's Playground: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1302, LocationData("Devil's Playground", "Devil's Playground: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1302,
lambda state: logic_level > 0 or state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player)), lambda state: logic_level > 0 or state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player)),
LocationData("Welcome to the Jungle", "Welcome to the Jungle: Victory", SC2WOL_LOC_ID_OFFSET + 1400, LocationData("Welcome to the Jungle", "Welcome to the Jungle: Victory", SC2WOL_LOC_ID_OFFSET + 1400,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player)), state._sc2wol_has_competent_anti_air(multiworld, player)),
LocationData("Welcome to the Jungle", "Welcome to the Jungle: Close Relic", SC2WOL_LOC_ID_OFFSET + 1401), LocationData("Welcome to the Jungle", "Welcome to the Jungle: Close Relic", SC2WOL_LOC_ID_OFFSET + 1401),
LocationData("Welcome to the Jungle", "Welcome to the Jungle: West Relic", SC2WOL_LOC_ID_OFFSET + 1402, LocationData("Welcome to the Jungle", "Welcome to the Jungle: West Relic", SC2WOL_LOC_ID_OFFSET + 1402,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player)), state._sc2wol_has_competent_anti_air(multiworld, player)),
LocationData("Welcome to the Jungle", "Welcome to the Jungle: North-East Relic", SC2WOL_LOC_ID_OFFSET + 1403, LocationData("Welcome to the Jungle", "Welcome to the Jungle: North-East Relic", SC2WOL_LOC_ID_OFFSET + 1403,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
state._sc2wol_has_competent_anti_air(world, player)), state._sc2wol_has_competent_anti_air(multiworld, player)),
LocationData("Breakout", "Breakout: Victory", SC2WOL_LOC_ID_OFFSET + 1500), LocationData("Breakout", "Breakout: Victory", SC2WOL_LOC_ID_OFFSET + 1500),
LocationData("Breakout", "Breakout: Diamondback Prison", SC2WOL_LOC_ID_OFFSET + 1501), LocationData("Breakout", "Breakout: Diamondback Prison", SC2WOL_LOC_ID_OFFSET + 1501),
LocationData("Breakout", "Breakout: Siegetank Prison", SC2WOL_LOC_ID_OFFSET + 1502), LocationData("Breakout", "Breakout: Siegetank Prison", SC2WOL_LOC_ID_OFFSET + 1502),
@ -178,101 +178,101 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
LocationData("Ghost of a Chance", "Ghost of a Chance: Second Island Spectres", SC2WOL_LOC_ID_OFFSET + 1604), LocationData("Ghost of a Chance", "Ghost of a Chance: Second Island Spectres", SC2WOL_LOC_ID_OFFSET + 1604),
LocationData("Ghost of a Chance", "Ghost of a Chance: Third Island Spectres", SC2WOL_LOC_ID_OFFSET + 1605), LocationData("Ghost of a Chance", "Ghost of a Chance: Third Island Spectres", SC2WOL_LOC_ID_OFFSET + 1605),
LocationData("The Great Train Robbery", "The Great Train Robbery: Victory", SC2WOL_LOC_ID_OFFSET + 1700, LocationData("The Great Train Robbery", "The Great Train Robbery: Victory", SC2WOL_LOC_ID_OFFSET + 1700,
lambda state: state._sc2wol_has_train_killers(world, player) and lambda state: state._sc2wol_has_train_killers(multiworld, player) and
state._sc2wol_has_anti_air(world, player)), state._sc2wol_has_anti_air(multiworld, player)),
LocationData("The Great Train Robbery", "The Great Train Robbery: North Defiler", SC2WOL_LOC_ID_OFFSET + 1701), LocationData("The Great Train Robbery", "The Great Train Robbery: North Defiler", SC2WOL_LOC_ID_OFFSET + 1701),
LocationData("The Great Train Robbery", "The Great Train Robbery: Mid Defiler", SC2WOL_LOC_ID_OFFSET + 1702), LocationData("The Great Train Robbery", "The Great Train Robbery: Mid Defiler", SC2WOL_LOC_ID_OFFSET + 1702),
LocationData("The Great Train Robbery", "The Great Train Robbery: South Defiler", SC2WOL_LOC_ID_OFFSET + 1703), LocationData("The Great Train Robbery", "The Great Train Robbery: South Defiler", SC2WOL_LOC_ID_OFFSET + 1703),
LocationData("Cutthroat", "Cutthroat: Victory", SC2WOL_LOC_ID_OFFSET + 1800, LocationData("Cutthroat", "Cutthroat: Victory", SC2WOL_LOC_ID_OFFSET + 1800,
lambda state: state._sc2wol_has_common_unit(world, player) and lambda state: state._sc2wol_has_common_unit(multiworld, player) and
(logic_level > 0 or state._sc2wol_has_anti_air)), (logic_level > 0 or state._sc2wol_has_anti_air)),
LocationData("Cutthroat", "Cutthroat: Mira Han", SC2WOL_LOC_ID_OFFSET + 1801, LocationData("Cutthroat", "Cutthroat: Mira Han", SC2WOL_LOC_ID_OFFSET + 1801,
lambda state: state._sc2wol_has_common_unit(world, player)), lambda state: state._sc2wol_has_common_unit(multiworld, player)),
LocationData("Cutthroat", "Cutthroat: North Relic", SC2WOL_LOC_ID_OFFSET + 1802, LocationData("Cutthroat", "Cutthroat: North Relic", SC2WOL_LOC_ID_OFFSET + 1802,
lambda state: state._sc2wol_has_common_unit(world, player)), lambda state: state._sc2wol_has_common_unit(multiworld, player)),
LocationData("Cutthroat", "Cutthroat: Mid Relic", SC2WOL_LOC_ID_OFFSET + 1803), LocationData("Cutthroat", "Cutthroat: Mid Relic", SC2WOL_LOC_ID_OFFSET + 1803),
LocationData("Cutthroat", "Cutthroat: Southwest Relic", SC2WOL_LOC_ID_OFFSET + 1804, LocationData("Cutthroat", "Cutthroat: Southwest Relic", SC2WOL_LOC_ID_OFFSET + 1804,
lambda state: state._sc2wol_has_common_unit(world, player)), lambda state: state._sc2wol_has_common_unit(multiworld, player)),
LocationData("Engine of Destruction", "Engine of Destruction: Victory", SC2WOL_LOC_ID_OFFSET + 1900, LocationData("Engine of Destruction", "Engine of Destruction: Victory", SC2WOL_LOC_ID_OFFSET + 1900,
lambda state: state._sc2wol_has_competent_anti_air(world, player) and lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)), state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
LocationData("Engine of Destruction", "Engine of Destruction: Odin", SC2WOL_LOC_ID_OFFSET + 1901), LocationData("Engine of Destruction", "Engine of Destruction: Odin", SC2WOL_LOC_ID_OFFSET + 1901),
LocationData("Engine of Destruction", "Engine of Destruction: Loki", SC2WOL_LOC_ID_OFFSET + 1902, LocationData("Engine of Destruction", "Engine of Destruction: Loki", SC2WOL_LOC_ID_OFFSET + 1902,
lambda state: state._sc2wol_has_competent_anti_air(world, player) and lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)), state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
LocationData("Engine of Destruction", "Engine of Destruction: Lab Devourer", SC2WOL_LOC_ID_OFFSET + 1903), LocationData("Engine of Destruction", "Engine of Destruction: Lab Devourer", SC2WOL_LOC_ID_OFFSET + 1903),
LocationData("Engine of Destruction", "Engine of Destruction: North Devourer", SC2WOL_LOC_ID_OFFSET + 1904, LocationData("Engine of Destruction", "Engine of Destruction: North Devourer", SC2WOL_LOC_ID_OFFSET + 1904,
lambda state: state._sc2wol_has_competent_anti_air(world, player) and lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)), state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
LocationData("Engine of Destruction", "Engine of Destruction: Southeast Devourer", SC2WOL_LOC_ID_OFFSET + 1905, LocationData("Engine of Destruction", "Engine of Destruction: Southeast Devourer", SC2WOL_LOC_ID_OFFSET + 1905,
lambda state: state._sc2wol_has_competent_anti_air(world, player) and lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)), state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
LocationData("Media Blitz", "Media Blitz: Victory", SC2WOL_LOC_ID_OFFSET + 2000, LocationData("Media Blitz", "Media Blitz: Victory", SC2WOL_LOC_ID_OFFSET + 2000,
lambda state: state._sc2wol_has_competent_comp(world, player)), lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
LocationData("Media Blitz", "Media Blitz: Tower 1", SC2WOL_LOC_ID_OFFSET + 2001, LocationData("Media Blitz", "Media Blitz: Tower 1", SC2WOL_LOC_ID_OFFSET + 2001,
lambda state: state._sc2wol_has_competent_comp(world, player)), lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
LocationData("Media Blitz", "Media Blitz: Tower 2", SC2WOL_LOC_ID_OFFSET + 2002, LocationData("Media Blitz", "Media Blitz: Tower 2", SC2WOL_LOC_ID_OFFSET + 2002,
lambda state: state._sc2wol_has_competent_comp(world, player)), lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
LocationData("Media Blitz", "Media Blitz: Tower 3", SC2WOL_LOC_ID_OFFSET + 2003, LocationData("Media Blitz", "Media Blitz: Tower 3", SC2WOL_LOC_ID_OFFSET + 2003,
lambda state: state._sc2wol_has_competent_comp(world, player)), lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
LocationData("Media Blitz", "Media Blitz: Science Facility", SC2WOL_LOC_ID_OFFSET + 2004), LocationData("Media Blitz", "Media Blitz: Science Facility", SC2WOL_LOC_ID_OFFSET + 2004),
LocationData("Piercing the Shroud", "Piercing the Shroud: Victory", SC2WOL_LOC_ID_OFFSET + 2100, LocationData("Piercing the Shroud", "Piercing the Shroud: Victory", SC2WOL_LOC_ID_OFFSET + 2100,
lambda state: state._sc2wol_has_mm_upgrade(world, player)), lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
LocationData("Piercing the Shroud", "Piercing the Shroud: Holding Cell Relic", SC2WOL_LOC_ID_OFFSET + 2101), LocationData("Piercing the Shroud", "Piercing the Shroud: Holding Cell Relic", SC2WOL_LOC_ID_OFFSET + 2101),
LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102, LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102,
lambda state: state._sc2wol_has_mm_upgrade(world, player)), lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103, LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103,
lambda state: state._sc2wol_has_mm_upgrade(world, player)), lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104, LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104,
lambda state: state._sc2wol_has_mm_upgrade(world, player)), lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk ", SC2WOL_LOC_ID_OFFSET + 2105, LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk ", SC2WOL_LOC_ID_OFFSET + 2105,
lambda state: state._sc2wol_has_mm_upgrade(world, player)), lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
LocationData("Whispers of Doom", "Whispers of Doom: Victory", SC2WOL_LOC_ID_OFFSET + 2200), LocationData("Whispers of Doom", "Whispers of Doom: Victory", SC2WOL_LOC_ID_OFFSET + 2200),
LocationData("Whispers of Doom", "Whispers of Doom: First Hatchery", SC2WOL_LOC_ID_OFFSET + 2201), LocationData("Whispers of Doom", "Whispers of Doom: First Hatchery", SC2WOL_LOC_ID_OFFSET + 2201),
LocationData("Whispers of Doom", "Whispers of Doom: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 2202), LocationData("Whispers of Doom", "Whispers of Doom: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 2202),
LocationData("Whispers of Doom", "Whispers of Doom: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 2203), LocationData("Whispers of Doom", "Whispers of Doom: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 2203),
LocationData("A Sinister Turn", "A Sinister Turn: Victory", SC2WOL_LOC_ID_OFFSET + 2300, LocationData("A Sinister Turn", "A Sinister Turn: Victory", SC2WOL_LOC_ID_OFFSET + 2300,
lambda state: state._sc2wol_has_protoss_medium_units(world, player)), lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
LocationData("A Sinister Turn", "A Sinister Turn: Robotics Facility", SC2WOL_LOC_ID_OFFSET + 2301, LocationData("A Sinister Turn", "A Sinister Turn: Robotics Facility", SC2WOL_LOC_ID_OFFSET + 2301,
lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(world, player)), lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)),
LocationData("A Sinister Turn", "A Sinister Turn: Dark Shrine", SC2WOL_LOC_ID_OFFSET + 2302, LocationData("A Sinister Turn", "A Sinister Turn: Dark Shrine", SC2WOL_LOC_ID_OFFSET + 2302,
lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(world, player)), lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)),
LocationData("A Sinister Turn", "A Sinister Turn: Templar Archives", SC2WOL_LOC_ID_OFFSET + 2303, LocationData("A Sinister Turn", "A Sinister Turn: Templar Archives", SC2WOL_LOC_ID_OFFSET + 2303,
lambda state: state._sc2wol_has_protoss_common_units(world, player)), lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)),
LocationData("Echoes of the Future", "Echoes of the Future: Victory", SC2WOL_LOC_ID_OFFSET + 2400, LocationData("Echoes of the Future", "Echoes of the Future: Victory", SC2WOL_LOC_ID_OFFSET + 2400,
lambda state: logic_level > 0 or state._sc2wol_has_protoss_medium_units(world, player)), lambda state: logic_level > 0 or state._sc2wol_has_protoss_medium_units(multiworld, player)),
LocationData("Echoes of the Future", "Echoes of the Future: Close Obelisk", SC2WOL_LOC_ID_OFFSET + 2401), LocationData("Echoes of the Future", "Echoes of the Future: Close Obelisk", SC2WOL_LOC_ID_OFFSET + 2401),
LocationData("Echoes of the Future", "Echoes of the Future: West Obelisk", SC2WOL_LOC_ID_OFFSET + 2402, LocationData("Echoes of the Future", "Echoes of the Future: West Obelisk", SC2WOL_LOC_ID_OFFSET + 2402,
lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(world, player)), lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)),
LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2500), LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2500),
LocationData("In Utter Darkness", "In Utter Darkness: Protoss Archive", SC2WOL_LOC_ID_OFFSET + 2501, LocationData("In Utter Darkness", "In Utter Darkness: Protoss Archive", SC2WOL_LOC_ID_OFFSET + 2501,
lambda state: state._sc2wol_has_protoss_medium_units(world, player)), lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2502, LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2502,
lambda state: state._sc2wol_has_protoss_common_units(world, player)), lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)),
LocationData("Gates of Hell", "Gates of Hell: Victory", SC2WOL_LOC_ID_OFFSET + 2600, LocationData("Gates of Hell", "Gates of Hell: Victory", SC2WOL_LOC_ID_OFFSET + 2600,
lambda state: state._sc2wol_has_competent_comp(world, player) and lambda state: state._sc2wol_has_competent_comp(multiworld, player) and
state._sc2wol_defense_rating(world, player, True) > 6), state._sc2wol_defense_rating(multiworld, player, True) > 6),
LocationData("Gates of Hell", "Gates of Hell: Large Army", SC2WOL_LOC_ID_OFFSET + 2601, LocationData("Gates of Hell", "Gates of Hell: Large Army", SC2WOL_LOC_ID_OFFSET + 2601,
lambda state: state._sc2wol_has_competent_comp(world, player) and lambda state: state._sc2wol_has_competent_comp(multiworld, player) and
state._sc2wol_defense_rating(world, player, True) > 6), state._sc2wol_defense_rating(multiworld, player, True) > 6),
LocationData("Belly of the Beast", "Belly of the Beast: Victory", SC2WOL_LOC_ID_OFFSET + 2700), LocationData("Belly of the Beast", "Belly of the Beast: Victory", SC2WOL_LOC_ID_OFFSET + 2700),
LocationData("Belly of the Beast", "Belly of the Beast: First Charge", SC2WOL_LOC_ID_OFFSET + 2701), LocationData("Belly of the Beast", "Belly of the Beast: First Charge", SC2WOL_LOC_ID_OFFSET + 2701),
LocationData("Belly of the Beast", "Belly of the Beast: Second Charge", SC2WOL_LOC_ID_OFFSET + 2702), LocationData("Belly of the Beast", "Belly of the Beast: Second Charge", SC2WOL_LOC_ID_OFFSET + 2702),
LocationData("Belly of the Beast", "Belly of the Beast: Third Charge", SC2WOL_LOC_ID_OFFSET + 2703), LocationData("Belly of the Beast", "Belly of the Beast: Third Charge", SC2WOL_LOC_ID_OFFSET + 2703),
LocationData("Shatter the Sky", "Shatter the Sky: Victory", SC2WOL_LOC_ID_OFFSET + 2800, LocationData("Shatter the Sky", "Shatter the Sky: Victory", SC2WOL_LOC_ID_OFFSET + 2800,
lambda state: state._sc2wol_has_competent_comp(world, player)), lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
LocationData("Shatter the Sky", "Shatter the Sky: Close Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2801, LocationData("Shatter the Sky", "Shatter the Sky: Close Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2801,
lambda state: state._sc2wol_has_competent_comp(world, player)), lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
LocationData("Shatter the Sky", "Shatter the Sky: Northwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2802, LocationData("Shatter the Sky", "Shatter the Sky: Northwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2802,
lambda state: state._sc2wol_has_competent_comp(world, player)), lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
LocationData("Shatter the Sky", "Shatter the Sky: Southeast Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2803, LocationData("Shatter the Sky", "Shatter the Sky: Southeast Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2803,
lambda state: state._sc2wol_has_competent_comp(world, player)), lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
LocationData("Shatter the Sky", "Shatter the Sky: Southwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2804, LocationData("Shatter the Sky", "Shatter the Sky: Southwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2804,
lambda state: state._sc2wol_has_competent_comp(world, player)), lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
LocationData("Shatter the Sky", "Shatter the Sky: Leviathan", SC2WOL_LOC_ID_OFFSET + 2805, LocationData("Shatter the Sky", "Shatter the Sky: Leviathan", SC2WOL_LOC_ID_OFFSET + 2805,
lambda state: state._sc2wol_has_competent_comp(world, player)), lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
LocationData("All-In", "All-In: Victory", None, LocationData("All-In", "All-In: Victory", None,
lambda state: state._sc2wol_final_mission_requirements(world, player)) lambda state: state._sc2wol_final_mission_requirements(multiworld, player))
] ]
beat_events = [] beat_events = []
@ -280,7 +280,8 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
for i, location_data in enumerate(location_table): for i, location_data in enumerate(location_table):
# Removing all item-based logic on No Logic # Removing all item-based logic on No Logic
if logic_level == 2: if logic_level == 2:
location_table[i] = location_data._replace(rule=Location.access_rule) location_data = location_data._replace(rule=Location.access_rule)
location_table[i] = location_data
# Generating Beat event locations # Generating Beat event locations
if location_data.name.endswith((": Victory", ": Defeat")): if location_data.name.endswith((": Victory", ": Defeat")):
beat_events.append( beat_events.append(

View File

@ -5,26 +5,26 @@ from .Items import get_basic_units, defense_ratings, zerg_defense_ratings
class SC2WoLLogic(LogicMixin): class SC2WoLLogic(LogicMixin):
def _sc2wol_has_common_unit(self, world: MultiWorld, player: int) -> bool: def _sc2wol_has_common_unit(self, multiworld: MultiWorld, player: int) -> bool:
return self.has_any(get_basic_units(world, player), player) return self.has_any(get_basic_units(multiworld, player), player)
def _sc2wol_has_air(self, world: MultiWorld, player: int) -> bool: def _sc2wol_has_air(self, multiworld: MultiWorld, player: int) -> bool:
return self.has_any({'Viking', 'Wraith', 'Banshee'}, player) or get_option_value(world, player, 'required_tactics') > 0 \ return self.has_any({'Viking', 'Wraith', 'Banshee'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 \
and self.has_any({'Hercules', 'Medivac'}, player) and self._sc2wol_has_common_unit(world, player) and self.has_any({'Hercules', 'Medivac'}, player) and self._sc2wol_has_common_unit(multiworld, player)
def _sc2wol_has_air_anti_air(self, world: MultiWorld, player: int) -> bool: def _sc2wol_has_air_anti_air(self, multiworld: MultiWorld, player: int) -> bool:
return self.has('Viking', player) \ return self.has('Viking', player) \
or get_option_value(world, player, 'required_tactics') > 0 and self.has('Wraith', player) or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has('Wraith', player)
def _sc2wol_has_competent_anti_air(self, world: MultiWorld, player: int) -> bool: 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(world, player) return self.has_any({'Marine', 'Goliath'}, player) or self._sc2wol_has_air_anti_air(multiworld, player)
def _sc2wol_has_anti_air(self, world: MultiWorld, player: int) -> bool: 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', 'Wraith'}, player) \
or self._sc2wol_has_competent_anti_air(world, player) \ or self._sc2wol_has_competent_anti_air(multiworld, player) \
or get_option_value(world, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player)
def _sc2wol_defense_rating(self, world: MultiWorld, player: int, zerg_enemy: bool, air_enemy: bool = True) -> bool: def _sc2wol_defense_rating(self, multiworld: MultiWorld, player: int, zerg_enemy: bool, air_enemy: bool = True) -> bool:
defense_score = sum((defense_ratings[item] for item in defense_ratings if self.has(item, player))) 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): if self.has_any({'Marine', 'Marauder'}, player) and self.has('Bunker', player):
defense_score += 3 defense_score += 3
@ -35,62 +35,63 @@ class SC2WoLLogic(LogicMixin):
if not air_enemy and self.has('Missile Turret', player): if not air_enemy and self.has('Missile Turret', player):
defense_score -= defense_ratings['Missile Turret'] defense_score -= defense_ratings['Missile Turret']
# Advanced Tactics bumps defense rating requirements down by 2 # Advanced Tactics bumps defense rating requirements down by 2
if get_option_value(world, player, 'required_tactics') > 0: if get_option_value(multiworld, player, 'required_tactics') > 0:
defense_score += 2 defense_score += 2
return defense_score return defense_score
def _sc2wol_has_competent_comp(self, world: MultiWorld, player: int) -> bool: def _sc2wol_has_competent_comp(self, multiworld: MultiWorld, player: int) -> bool:
return (self.has('Marine', player) or self.has('Marauder', player) and return (self.has('Marine', player) or self.has('Marauder', player) and
self._sc2wol_has_competent_anti_air(world, player)) and self.has_any({'Medivac', 'Medic'}, player) or \ self._sc2wol_has_competent_anti_air(multiworld, player)) and self.has_any({'Medivac', 'Medic'}, player) or \
self.has('Thor', player) or self.has("Banshee", player) and self._sc2wol_has_competent_anti_air(world, player) or \ self.has('Thor', player) or self.has("Banshee", player) and self._sc2wol_has_competent_anti_air(multiworld, player) or \
self.has('Battlecruiser', player) and self._sc2wol_has_common_unit(world, player) or \ self.has('Battlecruiser', player) and self._sc2wol_has_common_unit(multiworld, player) or \
self.has('Siege Tank', player) and self._sc2wol_has_competent_anti_air(world, player) self.has('Siege Tank', player) and self._sc2wol_has_competent_anti_air(multiworld, player)
def _sc2wol_has_train_killers(self, world: MultiWorld, player: int) -> bool: def _sc2wol_has_train_killers(self, multiworld: MultiWorld, player: int) -> bool:
return (self.has_any({'Siege Tank', 'Diamondback', 'Marauder'}, player) or get_option_value(world, player, 'required_tactics') > 0 return (self.has_any({'Siege Tank', 'Diamondback', 'Marauder'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0
and self.has_all({'Reaper', "G-4 Clusterbomb"}, player) or self.has_all({'Spectre', 'Psionic Lash'}, player)) and self.has_all({'Reaper', "G-4 Clusterbomb"}, player) or self.has_all({'Spectre', 'Psionic Lash'}, player))
def _sc2wol_able_to_rescue(self, world: MultiWorld, player: int) -> bool: def _sc2wol_able_to_rescue(self, multiworld: MultiWorld, player: int) -> bool:
return self.has_any({'Medivac', 'Hercules', 'Raven', 'Viking'}, player) or get_option_value(world, player, 'required_tactics') > 0 return self.has_any({'Medivac', 'Hercules', 'Raven', 'Viking'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0
def _sc2wol_has_protoss_common_units(self, world: MultiWorld, player: int) -> bool: def _sc2wol_has_protoss_common_units(self, multiworld: MultiWorld, player: int) -> bool:
return self.has_any({'Zealot', 'Immortal', 'Stalker', 'Dark Templar'}, player) \ return self.has_any({'Zealot', 'Immortal', 'Stalker', 'Dark Templar'}, player) \
or get_option_value(world, player, 'required_tactics') > 0 and self.has_any({'High Templar', 'Dark Templar'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'High Templar', 'Dark Templar'}, player)
def _sc2wol_has_protoss_medium_units(self, world: MultiWorld, player: int) -> bool: def _sc2wol_has_protoss_medium_units(self, multiworld: MultiWorld, player: int) -> bool:
return self._sc2wol_has_protoss_common_units(world, player) and \ return self._sc2wol_has_protoss_common_units(multiworld, player) and \
self.has_any({'Stalker', 'Void Ray', 'Phoenix', 'Carrier'}, player) \ self.has_any({'Stalker', 'Void Ray', 'Phoenix', 'Carrier'}, player) \
or get_option_value(world, player, 'required_tactics') > 0 and self.has_any({'High Templar', 'Dark Templar'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'High Templar', 'Dark Templar'}, player)
def _sc2wol_beats_protoss_deathball(self, world: MultiWorld, player: int) -> bool: def _sc2wol_beats_protoss_deathball(self, multiworld: MultiWorld, player: int) -> bool:
return self.has_any({'Banshee', 'Battlecruiser'}, player) and self._sc2wol_has_competent_anti_air or \ return self.has_any({'Banshee', 'Battlecruiser'}, player) and self._sc2wol_has_competent_anti_air(multiworld, player) or \
self._sc2wol_has_competent_comp(world, player) and self._sc2wol_has_air_anti_air(world, player) self._sc2wol_has_competent_comp(multiworld, player) and self._sc2wol_has_air_anti_air(multiworld, player)
def _sc2wol_has_mm_upgrade(self, world: MultiWorld, player: int) -> bool: def _sc2wol_has_mm_upgrade(self, multiworld: MultiWorld, player: int) -> bool:
return self.has_any({"Combat Shield (Marine)", "Stabilizer Medpacks (Medic)"}, player) return self.has_any({"Combat Shield (Marine)", "Stabilizer Medpacks (Medic)"}, player)
def _sc2wol_survives_rip_field(self, world: MultiWorld, player: int) -> bool: def _sc2wol_survives_rip_field(self, multiworld: MultiWorld, player: int) -> bool:
return self.has("Battlecruiser", player) or \ return self.has("Battlecruiser", player) or \
self._sc2wol_has_air(world, player) and \ self._sc2wol_has_air(multiworld, player) and \
self._sc2wol_has_competent_anti_air(world, player) and \ self._sc2wol_has_competent_anti_air(multiworld, player) and \
self.has("Science Vessel", player) self.has("Science Vessel", player)
def _sc2wol_has_nukes(self, world: MultiWorld, player: int) -> bool: def _sc2wol_has_nukes(self, multiworld: MultiWorld, player: int) -> bool:
return get_option_value(world, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player) return get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player)
def _sc2wol_final_mission_requirements(self, world: MultiWorld, player: int): def _sc2wol_final_mission_requirements(self, multiworld: MultiWorld, player: int):
defense_rating = self._sc2wol_defense_rating(world, player, True) beats_kerrigan = self.has_any({'Marine', 'Banshee', 'Ghost'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0
beats_kerrigan = self.has_any({'Marine', 'Banshee', 'Ghost'}, player) or get_option_value(world, player, 'required_tactics') > 0 if get_option_value(multiworld, player, 'all_in_map') == 0:
if get_option_value(world, player, 'all_in_map') == 0:
# Ground # Ground
defense_rating = self._sc2wol_defense_rating(multiworld, player, True, False)
if self.has_any({'Battlecruiser', 'Banshee'}, player): if self.has_any({'Battlecruiser', 'Banshee'}, player):
defense_rating += 3 defense_rating += 3
return defense_rating >= 12 and beats_kerrigan return defense_rating >= 12 and beats_kerrigan
else: else:
# Air # Air
defense_rating = self._sc2wol_defense_rating(multiworld, player, True, True)
return defense_rating >= 8 and beats_kerrigan \ return defense_rating >= 8 and beats_kerrigan \
and self.has_any({'Viking', 'Battlecruiser'}, player) \ and self.has_any({'Viking', 'Battlecruiser'}, player) \
and self.has_any({'Hive Mind Emulator', 'Psi Disruptor', 'Missile Turret'}, player) and self.has_any({'Hive Mind Emulator', 'Psi Disruptor', 'Missile Turret'}, player)
def _sc2wol_cleared_missions(self, world: MultiWorld, player: int, mission_count: int) -> bool: def _sc2wol_cleared_missions(self, multiworld: MultiWorld, player: int, mission_count: int) -> bool:
return self.has_group("Missions", player, mission_count) return self.has_group("Missions", player, mission_count)

View File

@ -197,12 +197,12 @@ advanced_starting_mission_locations = {
} }
def get_starting_mission_locations(world: MultiWorld, player: int) -> Set[str]: def get_starting_mission_locations(multiworld: MultiWorld, player: int) -> Set[str]:
if get_option_value(world, player, 'shuffle_no_build') or get_option_value(world, player, 'mission_order') < 2: 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 # Always start with a no-build mission unless explicitly relegating them
# Vanilla and Vanilla Shuffled always start with a no-build even when relegated # Vanilla and Vanilla Shuffled always start with a no-build even when relegated
return no_build_starting_mission_locations return no_build_starting_mission_locations
elif get_option_value(world, player, 'required_tactics') > 0: elif get_option_value(multiworld, player, 'required_tactics') > 0:
# Advanced Tactics/No Logic add more starting missions to the pool # Advanced Tactics/No Logic add more starting missions to the pool
return {**build_starting_mission_locations, **advanced_starting_mission_locations} return {**build_starting_mission_locations, **advanced_starting_mission_locations}
else: else:

View File

@ -130,8 +130,8 @@ sc2wol_options: Dict[str, Option] = {
} }
def get_option_value(world: MultiWorld, player: int, name: str) -> int: def get_option_value(multiworld: MultiWorld, player: int, name: str) -> int:
option = getattr(world, name, None) option = getattr(multiworld, name, None)
if option is None: if option is None:
return 0 return 0
@ -139,8 +139,8 @@ def get_option_value(world: MultiWorld, player: int, name: str) -> int:
return int(option[player].value) return int(option[player].value)
def get_option_set_value(world: MultiWorld, player: int, name: str) -> set: def get_option_set_value(multiworld: MultiWorld, player: int, name: str) -> set:
option = getattr(world, name, None) option = getattr(multiworld, name, None)
if option is None: if option is None:
return set() return set()

View File

@ -21,14 +21,14 @@ STARPORT_UNITS = {"Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", "He
PROTOSS_REGIONS = {"A Sinister Turn", "Echoes of the Future", "In Utter Darkness"} PROTOSS_REGIONS = {"A Sinister Turn", "Echoes of the Future", "In Utter Darkness"}
def filter_missions(world: MultiWorld, player: int) -> Dict[str, List[str]]: def filter_missions(multiworld: MultiWorld, player: int) -> Dict[str, List[str]]:
""" """
Returns a semi-randomly pruned tuple of no-build, easy, medium, and hard mission sets Returns a semi-randomly pruned tuple of no-build, easy, medium, and hard mission sets
""" """
mission_order_type = get_option_value(world, player, "mission_order") mission_order_type = get_option_value(multiworld, player, "mission_order")
shuffle_protoss = get_option_value(world, player, "shuffle_protoss") shuffle_protoss = get_option_value(multiworld, player, "shuffle_protoss")
excluded_missions = set(get_option_set_value(world, player, "excluded_missions")) excluded_missions = set(get_option_set_value(multiworld, player, "excluded_missions"))
invalid_mission_names = excluded_missions.difference(vanilla_mission_req_table.keys()) invalid_mission_names = excluded_missions.difference(vanilla_mission_req_table.keys())
if invalid_mission_names: if invalid_mission_names:
raise Exception("Error in locked_missions - the following are not valid mission names: " + ", ".join(invalid_mission_names)) raise Exception("Error in locked_missions - the following are not valid mission names: " + ", ".join(invalid_mission_names))
@ -54,17 +54,17 @@ def filter_missions(world: MultiWorld, player: int) -> Dict[str, List[str]]:
excluded_missions = excluded_missions.union(PROTOSS_REGIONS) excluded_missions = excluded_missions.union(PROTOSS_REGIONS)
# Replacing All-In on low mission counts # Replacing All-In on low mission counts
if mission_count < 14: if mission_count < 14:
final_mission = world.random.choice([mission for mission in alt_final_mission_locations.keys() if mission not in excluded_missions]) final_mission = multiworld.random.choice([mission for mission in alt_final_mission_locations.keys() if mission not in excluded_missions])
excluded_missions.add(final_mission) excluded_missions.add(final_mission)
else: else:
final_mission = 'All-In' final_mission = 'All-In'
# Yaml settings determine which missions can be placed in the first slot # Yaml settings determine which missions can be placed in the first slot
mission_pools[0] = [mission for mission in get_starting_mission_locations(world, player).keys() if mission not in excluded_missions] 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 # Removing the new no-build missions from their original sets
for i in range(1, len(mission_pools)): 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])] 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 # If the first mission is a build mission, there may not be enough locations to reach Outbreak as a second mission
if not get_option_value(world, player, 'shuffle_no_build'): if not get_option_value(multiworld, player, 'shuffle_no_build'):
# Swapping Outbreak and The Great Train Robbery # Swapping Outbreak and The Great Train Robbery
if "Outbreak" in mission_pools[1]: if "Outbreak" in mission_pools[1]:
mission_pools[1].remove("Outbreak") mission_pools[1].remove("Outbreak")
@ -87,7 +87,7 @@ def filter_missions(world: MultiWorld, player: int) -> Dict[str, List[str]]:
if all(len(mission_pool) <= 1 for mission_pool in mission_pools): 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.") raise Exception("Not enough missions available to fill the campaign on current settings. Please exclude fewer missions.")
else: else:
mission_pool.remove(world.random.choice(mission_pool)) mission_pool.remove(multiworld.random.choice(mission_pool))
current_count -= 1 current_count -= 1
set_cycle += 1 set_cycle += 1
@ -134,7 +134,7 @@ class ValidInventory:
} }
requirements = mission_requirements requirements = mission_requirements
cascade_keys = self.cascade_removal_map.keys() cascade_keys = self.cascade_removal_map.keys()
units_always_have_upgrades = get_option_value(self.world, self.player, "units_always_have_upgrades") units_always_have_upgrades = get_option_value(self.multiworld, self.player, "units_always_have_upgrades")
if self.min_units_per_structure > 0: if self.min_units_per_structure > 0:
requirements.append(lambda state: state.has_units_per_structure()) requirements.append(lambda state: state.has_units_per_structure())
@ -155,7 +155,7 @@ class ValidInventory:
if len(inventory) == 0: if len(inventory) == 0:
raise Exception("Reduced item pool generation failed - not enough locations available to place items.") raise Exception("Reduced item pool generation failed - not enough locations available to place items.")
# Select random item from removable items # Select random item from removable items
item = self.world.random.choice(inventory) item = self.multiworld.random.choice(inventory)
# Cascade removals to associated items # Cascade removals to associated items
if item in cascade_keys: if item in cascade_keys:
items_to_remove = self.cascade_removal_map[item] items_to_remove = self.cascade_removal_map[item]
@ -206,10 +206,10 @@ class ValidInventory:
self._sc2wol_has_mm_upgrade = lambda world, player: SC2WoLLogic._sc2wol_has_mm_upgrade(self, world, player) self._sc2wol_has_mm_upgrade = lambda world, player: SC2WoLLogic._sc2wol_has_mm_upgrade(self, world, player)
self._sc2wol_final_mission_requirements = lambda world, player: SC2WoLLogic._sc2wol_final_mission_requirements(self, world, player) self._sc2wol_final_mission_requirements = lambda world, player: SC2WoLLogic._sc2wol_final_mission_requirements(self, world, player)
def __init__(self, world: MultiWorld, player: int, def __init__(self, multiworld: MultiWorld, player: int,
item_pool: List[Item], existing_items: List[Item], locked_items: List[Item], item_pool: List[Item], existing_items: List[Item], locked_items: List[Item],
has_protoss: bool): has_protoss: bool):
self.world = world self.multiworld = multiworld
self.player = player self.player = player
self.logical_inventory = set() self.logical_inventory = set()
self.locked_items = locked_items[:] self.locked_items = locked_items[:]
@ -219,7 +219,7 @@ class ValidInventory:
self.item_pool = [] self.item_pool = []
item_quantities: dict[str, int] = dict() item_quantities: dict[str, int] = dict()
# Inventory restrictiveness based on number of missions with checks # Inventory restrictiveness based on number of missions with checks
mission_order_type = get_option_value(self.world, self.player, "mission_order") mission_order_type = get_option_value(self.multiworld, self.player, "mission_order")
mission_count = len(mission_orders[mission_order_type]) - 1 mission_count = len(mission_orders[mission_order_type]) - 1
self.min_units_per_structure = int(mission_count / 7) self.min_units_per_structure = int(mission_count / 7)
min_upgrades = 1 if mission_count < 10 else 2 min_upgrades = 1 if mission_count < 10 else 2
@ -244,12 +244,12 @@ class ValidInventory:
upgrades = get_item_upgrades(self.item_pool, item) upgrades = get_item_upgrades(self.item_pool, item)
associated_items = [*upgrades, item] associated_items = [*upgrades, item]
self.cascade_removal_map[item] = associated_items self.cascade_removal_map[item] = associated_items
if get_option_value(world, player, "units_always_have_upgrades"): if get_option_value(multiworld, player, "units_always_have_upgrades"):
for upgrade in upgrades: for upgrade in upgrades:
self.cascade_removal_map[upgrade] = associated_items self.cascade_removal_map[upgrade] = associated_items
def filter_items(world: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo], location_cache: List[Location], def filter_items(multiworld: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo], location_cache: List[Location],
item_pool: List[Item], existing_items: List[Item], locked_items: List[Item]) -> List[Item]: item_pool: List[Item], existing_items: List[Item], locked_items: List[Item]) -> List[Item]:
""" """
Returns a semi-randomly pruned set of items based on number of available locations. Returns a semi-randomly pruned set of items based on number of available locations.
@ -259,7 +259,7 @@ def filter_items(world: MultiWorld, player: int, mission_req_table: Dict[str, Mi
inventory_size = len(open_locations) inventory_size = len(open_locations)
has_protoss = bool(PROTOSS_REGIONS.intersection(mission_req_table.keys())) has_protoss = bool(PROTOSS_REGIONS.intersection(mission_req_table.keys()))
mission_requirements = [location.access_rule for location in location_cache] mission_requirements = [location.access_rule for location in location_cache]
valid_inventory = ValidInventory(world, player, item_pool, existing_items, locked_items, has_protoss) valid_inventory = ValidInventory(multiworld, player, item_pool, existing_items, locked_items, has_protoss)
valid_items = valid_inventory.generate_reduced_inventory(inventory_size, mission_requirements) valid_items = valid_inventory.generate_reduced_inventory(inventory_size, mission_requirements)
return valid_items return valid_items

View File

@ -4,23 +4,22 @@ from .Locations import LocationData
from .Options import get_option_value 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
from .PoolFilter import filter_missions from .PoolFilter import filter_missions
import random
def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location])\ def create_regions(multiworld: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location])\
-> Tuple[Dict[str, MissionInfo], int, str]: -> Tuple[Dict[str, MissionInfo], int, str]:
locations_per_region = get_locations_per_region(locations) locations_per_region = get_locations_per_region(locations)
mission_order_type = get_option_value(world, player, "mission_order") mission_order_type = get_option_value(multiworld, player, "mission_order")
mission_order = mission_orders[mission_order_type] mission_order = mission_orders[mission_order_type]
mission_pools = filter_missions(world, player) mission_pools = filter_missions(multiworld, player)
final_mission = mission_pools['all_in'][0] final_mission = mission_pools['all_in'][0]
used_regions = [mission for mission_pool in mission_pools.values() for mission in mission_pool] used_regions = [mission for mission_pool in mission_pools.values() for mission in mission_pool]
regions = [create_region(world, player, locations_per_region, location_cache, "Menu")] regions = [create_region(multiworld, player, locations_per_region, location_cache, "Menu")]
for region_name in used_regions: for region_name in used_regions:
regions.append(create_region(world, player, locations_per_region, location_cache, region_name)) regions.append(create_region(multiworld, player, locations_per_region, location_cache, region_name))
# Changing the completion condition for alternate final missions into an event # Changing the completion condition for alternate final missions into an event
if final_mission != 'All-In': if final_mission != 'All-In':
final_location = alt_final_mission_locations[final_mission] final_location = alt_final_mission_locations[final_mission]
@ -38,76 +37,76 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
if mission_order_type in (0, 1): if mission_order_type in (0, 1):
throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys()) throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys())
world.regions += regions multiworld.regions += regions
names: Dict[str, int] = {} names: Dict[str, int] = {}
if mission_order_type == 0: if mission_order_type == 0:
connect(world, player, names, 'Menu', 'Liberation Day'), connect(multiworld, player, names, 'Menu', 'Liberation Day'),
connect(world, player, names, 'Liberation Day', 'The Outlaws', connect(multiworld, player, names, 'Liberation Day', 'The Outlaws',
lambda state: state.has("Beat Liberation Day", player)), lambda state: state.has("Beat Liberation Day", player)),
connect(world, player, names, 'The Outlaws', 'Zero Hour', connect(multiworld, player, names, 'The Outlaws', 'Zero Hour',
lambda state: state.has("Beat The Outlaws", player)), lambda state: state.has("Beat The Outlaws", player)),
connect(world, player, names, 'Zero Hour', 'Evacuation', connect(multiworld, player, names, 'Zero Hour', 'Evacuation',
lambda state: state.has("Beat Zero Hour", player)), lambda state: state.has("Beat Zero Hour", player)),
connect(world, player, names, 'Evacuation', 'Outbreak', connect(multiworld, player, names, 'Evacuation', 'Outbreak',
lambda state: state.has("Beat Evacuation", player)), lambda state: state.has("Beat Evacuation", player)),
connect(world, player, names, "Outbreak", "Safe Haven", connect(multiworld, player, names, "Outbreak", "Safe Haven",
lambda state: state._sc2wol_cleared_missions(world, player, 7) and lambda state: state._sc2wol_cleared_missions(multiworld, player, 7) and
state.has("Beat Outbreak", player)), state.has("Beat Outbreak", player)),
connect(world, player, names, "Outbreak", "Haven's Fall", connect(multiworld, player, names, "Outbreak", "Haven's Fall",
lambda state: state._sc2wol_cleared_missions(world, player, 7) and lambda state: state._sc2wol_cleared_missions(multiworld, player, 7) and
state.has("Beat Outbreak", player)), state.has("Beat Outbreak", player)),
connect(world, player, names, 'Zero Hour', 'Smash and Grab', connect(multiworld, player, names, 'Zero Hour', 'Smash and Grab',
lambda state: state.has("Beat Zero Hour", player)), lambda state: state.has("Beat Zero Hour", player)),
connect(world, player, names, 'Smash and Grab', 'The Dig', connect(multiworld, player, names, 'Smash and Grab', 'The Dig',
lambda state: state._sc2wol_cleared_missions(world, player, 8) and lambda state: state._sc2wol_cleared_missions(multiworld, player, 8) and
state.has("Beat Smash and Grab", player)), state.has("Beat Smash and Grab", player)),
connect(world, player, names, 'The Dig', 'The Moebius Factor', connect(multiworld, player, names, 'The Dig', 'The Moebius Factor',
lambda state: state._sc2wol_cleared_missions(world, player, 11) and lambda state: state._sc2wol_cleared_missions(multiworld, player, 11) and
state.has("Beat The Dig", player)), state.has("Beat The Dig", player)),
connect(world, player, names, 'The Moebius Factor', 'Supernova', connect(multiworld, player, names, 'The Moebius Factor', 'Supernova',
lambda state: state._sc2wol_cleared_missions(world, player, 14) and lambda state: state._sc2wol_cleared_missions(multiworld, player, 14) and
state.has("Beat The Moebius Factor", player)), state.has("Beat The Moebius Factor", player)),
connect(world, player, names, 'Supernova', 'Maw of the Void', connect(multiworld, player, names, 'Supernova', 'Maw of the Void',
lambda state: state.has("Beat Supernova", player)), lambda state: state.has("Beat Supernova", player)),
connect(world, player, names, 'Zero Hour', "Devil's Playground", connect(multiworld, player, names, 'Zero Hour', "Devil's Playground",
lambda state: state._sc2wol_cleared_missions(world, player, 4) and lambda state: state._sc2wol_cleared_missions(multiworld, player, 4) and
state.has("Beat Zero Hour", player)), state.has("Beat Zero Hour", player)),
connect(world, player, names, "Devil's Playground", 'Welcome to the Jungle', connect(multiworld, player, names, "Devil's Playground", 'Welcome to the Jungle',
lambda state: state.has("Beat Devil's Playground", player)), lambda state: state.has("Beat Devil's Playground", player)),
connect(world, player, names, "Welcome to the Jungle", 'Breakout', connect(multiworld, player, names, "Welcome to the Jungle", 'Breakout',
lambda state: state._sc2wol_cleared_missions(world, player, 8) and lambda state: state._sc2wol_cleared_missions(multiworld, player, 8) and
state.has("Beat Welcome to the Jungle", player)), state.has("Beat Welcome to the Jungle", player)),
connect(world, player, names, "Welcome to the Jungle", 'Ghost of a Chance', connect(multiworld, player, names, "Welcome to the Jungle", 'Ghost of a Chance',
lambda state: state._sc2wol_cleared_missions(world, player, 8) and lambda state: state._sc2wol_cleared_missions(multiworld, player, 8) and
state.has("Beat Welcome to the Jungle", player)), state.has("Beat Welcome to the Jungle", player)),
connect(world, player, names, "Zero Hour", 'The Great Train Robbery', connect(multiworld, player, names, "Zero Hour", 'The Great Train Robbery',
lambda state: state._sc2wol_cleared_missions(world, player, 6) and lambda state: state._sc2wol_cleared_missions(multiworld, player, 6) and
state.has("Beat Zero Hour", player)), state.has("Beat Zero Hour", player)),
connect(world, player, names, 'The Great Train Robbery', 'Cutthroat', connect(multiworld, player, names, 'The Great Train Robbery', 'Cutthroat',
lambda state: state.has("Beat The Great Train Robbery", player)), lambda state: state.has("Beat The Great Train Robbery", player)),
connect(world, player, names, 'Cutthroat', 'Engine of Destruction', connect(multiworld, player, names, 'Cutthroat', 'Engine of Destruction',
lambda state: state.has("Beat Cutthroat", player)), lambda state: state.has("Beat Cutthroat", player)),
connect(world, player, names, 'Engine of Destruction', 'Media Blitz', connect(multiworld, player, names, 'Engine of Destruction', 'Media Blitz',
lambda state: state.has("Beat Engine of Destruction", player)), lambda state: state.has("Beat Engine of Destruction", player)),
connect(world, player, names, 'Media Blitz', 'Piercing the Shroud', connect(multiworld, player, names, 'Media Blitz', 'Piercing the Shroud',
lambda state: state.has("Beat Media Blitz", player)), lambda state: state.has("Beat Media Blitz", player)),
connect(world, player, names, 'The Dig', 'Whispers of Doom', connect(multiworld, player, names, 'The Dig', 'Whispers of Doom',
lambda state: state.has("Beat The Dig", player)), lambda state: state.has("Beat The Dig", player)),
connect(world, player, names, 'Whispers of Doom', 'A Sinister Turn', connect(multiworld, player, names, 'Whispers of Doom', 'A Sinister Turn',
lambda state: state.has("Beat Whispers of Doom", player)), lambda state: state.has("Beat Whispers of Doom", player)),
connect(world, player, names, 'A Sinister Turn', 'Echoes of the Future', connect(multiworld, player, names, 'A Sinister Turn', 'Echoes of the Future',
lambda state: state.has("Beat A Sinister Turn", player)), lambda state: state.has("Beat A Sinister Turn", player)),
connect(world, player, names, 'Echoes of the Future', 'In Utter Darkness', connect(multiworld, player, names, 'Echoes of the Future', 'In Utter Darkness',
lambda state: state.has("Beat Echoes of the Future", player)), lambda state: state.has("Beat Echoes of the Future", player)),
connect(world, player, names, 'Maw of the Void', 'Gates of Hell', connect(multiworld, player, names, 'Maw of the Void', 'Gates of Hell',
lambda state: state.has("Beat Maw of the Void", player)), lambda state: state.has("Beat Maw of the Void", player)),
connect(world, player, names, 'Gates of Hell', 'Belly of the Beast', connect(multiworld, player, names, 'Gates of Hell', 'Belly of the Beast',
lambda state: state.has("Beat Gates of Hell", player)), lambda state: state.has("Beat Gates of Hell", player)),
connect(world, player, names, 'Gates of Hell', 'Shatter the Sky', connect(multiworld, player, names, 'Gates of Hell', 'Shatter the Sky',
lambda state: state.has("Beat Gates of Hell", player)), lambda state: state.has("Beat Gates of Hell", player)),
connect(world, player, names, 'Gates of Hell', 'All-In', connect(multiworld, player, names, 'Gates of Hell', 'All-In',
lambda state: state.has('Beat Gates of Hell', player) and ( 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))) state.has('Beat Shatter the Sky', player) or state.has('Beat Belly of the Beast', player)))
@ -122,13 +121,13 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
missions.append(None) missions.append(None)
elif mission.type == "all_in": elif mission.type == "all_in":
missions.append(final_mission) missions.append(final_mission)
elif mission.relegate and not get_option_value(world, player, "shuffle_no_build"): elif mission.relegate and not get_option_value(multiworld, player, "shuffle_no_build"):
missions.append("no_build") missions.append("no_build")
else: else:
missions.append(mission.type) missions.append(mission.type)
# Place Protoss Missions if we are not using ShuffleProtoss and are in Vanilla Shuffled # Place Protoss Missions if we are not using ShuffleProtoss and are in Vanilla Shuffled
if get_option_value(world, player, "shuffle_protoss") == 0 and mission_order_type == 1: if get_option_value(multiworld, player, "shuffle_protoss") == 0 and mission_order_type == 1:
missions[22] = "A Sinister Turn" missions[22] = "A Sinister Turn"
mission_pools['medium'].remove("A Sinister Turn") mission_pools['medium'].remove("A Sinister Turn")
missions[23] = "Echoes of the Future" missions[23] = "Echoes of the Future"
@ -157,28 +156,28 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
# Add no_build missions to the pool and fill in no_build slots # 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['no_build']
for slot in no_build_slots: for slot in no_build_slots:
filler = world.random.randint(0, len(missions_to_add)-1) filler = multiworld.random.randint(0, len(missions_to_add) - 1)
missions[slot] = missions_to_add.pop(filler) missions[slot] = missions_to_add.pop(filler)
# Add easy missions into pool and fill in easy slots # 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['easy']
for slot in easy_slots: for slot in easy_slots:
filler = world.random.randint(0, len(missions_to_add) - 1) filler = multiworld.random.randint(0, len(missions_to_add) - 1)
missions[slot] = missions_to_add.pop(filler) missions[slot] = missions_to_add.pop(filler)
# Add medium missions into pool and fill in medium slots # 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['medium']
for slot in medium_slots: for slot in medium_slots:
filler = world.random.randint(0, len(missions_to_add) - 1) filler = multiworld.random.randint(0, len(missions_to_add) - 1)
missions[slot] = missions_to_add.pop(filler) missions[slot] = missions_to_add.pop(filler)
# Add hard missions into pool and fill in hard slots # 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['hard']
for slot in hard_slots: for slot in hard_slots:
filler = world.random.randint(0, len(missions_to_add) - 1) filler = multiworld.random.randint(0, len(missions_to_add) - 1)
missions[slot] = missions_to_add.pop(filler) missions[slot] = missions_to_add.pop(filler)
@ -189,11 +188,11 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
connections = [] connections = []
for connection in mission_order[i].connect_to: for connection in mission_order[i].connect_to:
if connection == -1: if connection == -1:
connect(world, player, names, "Menu", missions[i]) connect(multiworld, player, names, "Menu", missions[i])
else: else:
connect(world, player, names, missions[connection], missions[i], connect(multiworld, player, names, missions[connection], missions[i],
(lambda name, missions_req: (lambda state: state.has(f"Beat {name}", player) and (lambda name, missions_req: (lambda state: state.has(f"Beat {name}", player) and
state._sc2wol_cleared_missions(world, player, state._sc2wol_cleared_missions(multiworld, player,
missions_req))) missions_req)))
(missions[connection], mission_order[i].number)) (missions[connection], mission_order[i].number))
connections.append(connection + 1) connections.append(connection + 1)
@ -233,10 +232,10 @@ def create_location(player: int, location_data: LocationData, region: Region,
return location return location
def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], def create_region(multiworld: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]],
location_cache: List[Location], name: str) -> Region: location_cache: List[Location], name: str) -> Region:
region = Region(name, RegionType.Generic, name, player) region = Region(name, RegionType.Generic, name, player)
region.multiworld = world region.multiworld = multiworld
if name in locations_per_region: if name in locations_per_region:
for location_data in locations_per_region[name]: for location_data in locations_per_region[name]:

View File

@ -48,8 +48,8 @@ class SC2WoLWorld(World):
victory_item: str victory_item: str
required_client_version = 0, 3, 6 required_client_version = 0, 3, 6
def __init__(self, world: MultiWorld, player: int): def __init__(self, multiworld: MultiWorld, player: int):
super(SC2WoLWorld, self).__init__(world, player) super(SC2WoLWorld, self).__init__(multiworld, player)
self.location_cache = [] self.location_cache = []
self.locked_locations = [] self.locked_locations = []
@ -63,7 +63,7 @@ class SC2WoLWorld(World):
) )
def generate_basic(self): def generate_basic(self):
excluded_items = get_excluded_items(self, self.multiworld, self.player) excluded_items = get_excluded_items(self.multiworld, self.player)
starter_items = assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations) starter_items = assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations)
@ -74,7 +74,7 @@ class SC2WoLWorld(World):
self.multiworld.itempool += pool self.multiworld.itempool += pool
def set_rules(self): def set_rules(self):
setup_events(self.multiworld, self.player, self.locked_locations, self.location_cache) setup_events(self.player, self.locked_locations, self.location_cache)
self.multiworld.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player) self.multiworld.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player)
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
@ -95,7 +95,7 @@ class SC2WoLWorld(World):
return slot_data return slot_data
def setup_events(world: MultiWorld, player: int, locked_locations: typing.List[str], location_cache: typing.List[Location]): def setup_events(player: int, locked_locations: typing.List[str], location_cache: typing.List[Location]):
for location in location_cache: for location in location_cache:
if location.address is None: if location.address is None:
item = Item(location.name, ItemClassification.progression, None, player) item = Item(location.name, ItemClassification.progression, None, player)
@ -105,39 +105,39 @@ def setup_events(world: MultiWorld, player: int, locked_locations: typing.List[s
location.place_locked_item(item) location.place_locked_item(item)
def get_excluded_items(self: SC2WoLWorld, world: MultiWorld, player: int) -> Set[str]: def get_excluded_items(multiworld: MultiWorld, player: int) -> Set[str]:
excluded_items: Set[str] = set() excluded_items: Set[str] = set()
if get_option_value(world, player, "upgrade_bonus") == 1: if get_option_value(multiworld, player, "upgrade_bonus") == 1:
excluded_items.add("Ultra-Capacitors") excluded_items.add("Ultra-Capacitors")
else: else:
excluded_items.add("Vanadium Plating") excluded_items.add("Vanadium Plating")
if get_option_value(world, player, "bunker_upgrade") == 1: if get_option_value(multiworld, player, "bunker_upgrade") == 1:
excluded_items.add("Shrike Turret") excluded_items.add("Shrike Turret")
else: else:
excluded_items.add("Fortified Bunker") excluded_items.add("Fortified Bunker")
for item in world.precollected_items[player]: for item in multiworld.precollected_items[player]:
excluded_items.add(item.name) excluded_items.add(item.name)
excluded_items_option = getattr(world, 'excluded_items', []) excluded_items_option = getattr(multiworld, 'excluded_items', [])
excluded_items.update(excluded_items_option[player].value) excluded_items.update(excluded_items_option[player].value)
return excluded_items return excluded_items
def assign_starter_items(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str]) -> List[Item]: def assign_starter_items(multiworld: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str]) -> List[Item]:
non_local_items = world.non_local_items[player].value non_local_items = multiworld.non_local_items[player].value
if get_option_value(world, player, "early_unit"): if get_option_value(multiworld, player, "early_unit"):
local_basic_unit = tuple(item for item in get_basic_units(world, player) if item not in non_local_items and item not in excluded_items) local_basic_unit = sorted(item for item in get_basic_units(multiworld, player) if item not in non_local_items and item not in excluded_items)
if not local_basic_unit: if not local_basic_unit:
raise Exception("At least one basic unit must be local") raise Exception("At least one basic unit must be local")
# The first world should also be the starting world # The first world should also be the starting world
first_mission = list(world.worlds[player].mission_req_table)[0] first_mission = list(multiworld.worlds[player].mission_req_table)[0]
starting_mission_locations = get_starting_mission_locations(world, player) starting_mission_locations = get_starting_mission_locations(multiworld, player)
if first_mission in starting_mission_locations: if first_mission in starting_mission_locations:
first_location = starting_mission_locations[first_mission] first_location = starting_mission_locations[first_mission]
elif first_mission == "In Utter Darkness": elif first_mission == "In Utter Darkness":
@ -145,28 +145,28 @@ def assign_starter_items(world: MultiWorld, player: int, excluded_items: Set[str
else: else:
first_location = first_mission + ": Victory" first_location = first_mission + ": Victory"
return [assign_starter_item(world, player, excluded_items, locked_locations, first_location, local_basic_unit)] return [assign_starter_item(multiworld, player, excluded_items, locked_locations, first_location, local_basic_unit)]
else: else:
return [] return []
def assign_starter_item(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str], def assign_starter_item(multiworld: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str],
location: str, item_list: Tuple[str, ...]) -> Item: location: str, item_list: Tuple[str, ...]) -> Item:
item_name = world.random.choice(item_list) item_name = multiworld.random.choice(item_list)
excluded_items.add(item_name) excluded_items.add(item_name)
item = create_item_with_correct_settings(world, player, item_name) item = create_item_with_correct_settings(player, item_name)
world.get_location(location, player).place_locked_item(item) multiworld.get_location(location, player).place_locked_item(item)
locked_locations.append(location) locked_locations.append(location)
return item return item
def get_item_pool(world: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo], def get_item_pool(multiworld: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo],
starter_items: List[str], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]: starter_items: List[str], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]:
pool: List[Item] = [] pool: List[Item] = []
@ -174,18 +174,18 @@ def get_item_pool(world: MultiWorld, player: int, mission_req_table: Dict[str, M
locked_items = [] locked_items = []
# YAML items # YAML items
yaml_locked_items = get_option_set_value(world, player, 'locked_items') yaml_locked_items = get_option_set_value(multiworld, player, 'locked_items')
for name, data in item_table.items(): for name, data in item_table.items():
if name not in excluded_items: if name not in excluded_items:
for _ in range(data.quantity): for _ in range(data.quantity):
item = create_item_with_correct_settings(world, player, name) item = create_item_with_correct_settings(player, name)
if name in yaml_locked_items: if name in yaml_locked_items:
locked_items.append(item) locked_items.append(item)
else: else:
pool.append(item) pool.append(item)
existing_items = starter_items + [item for item in world.precollected_items[player]] existing_items = starter_items + [item for item in multiworld.precollected_items[player]]
existing_names = [item.name for item in existing_items] existing_names = [item.name for item in existing_items]
# Removing upgrades for excluded items # Removing upgrades for excluded items
for item_name in excluded_items: for item_name in excluded_items:
@ -195,18 +195,18 @@ def get_item_pool(world: MultiWorld, player: int, mission_req_table: Dict[str, M
for invalid_upgrade in invalid_upgrades: for invalid_upgrade in invalid_upgrades:
pool.remove(invalid_upgrade) pool.remove(invalid_upgrade)
filtered_pool = filter_items(world, player, mission_req_table, location_cache, pool, existing_items, locked_items) filtered_pool = filter_items(multiworld, player, mission_req_table, location_cache, pool, existing_items, locked_items)
return filtered_pool return filtered_pool
def fill_item_pool_with_dummy_items(self: SC2WoLWorld, world: MultiWorld, player: int, locked_locations: List[str], def fill_item_pool_with_dummy_items(self: SC2WoLWorld, multiworld: MultiWorld, player: int, locked_locations: List[str],
location_cache: List[Location], pool: List[Item]): location_cache: List[Location], pool: List[Item]):
for _ in range(len(location_cache) - len(locked_locations) - len(pool)): for _ in range(len(location_cache) - len(locked_locations) - len(pool)):
item = create_item_with_correct_settings(world, player, self.get_filler_item_name()) item = create_item_with_correct_settings(player, self.get_filler_item_name())
pool.append(item) pool.append(item)
def create_item_with_correct_settings(world: MultiWorld, player: int, name: str) -> Item: def create_item_with_correct_settings(player: int, name: str) -> Item:
data = item_table[name] data = item_table[name]
item = Item(name, data.classification, data.code, player) item = Item(name, data.classification, data.code, player)