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:
parent
e3f169b4c3
commit
e71ea94fe5
|
@ -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])}/"
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue