Skip to content

Commit

Permalink
SC2: Adding race-swapping with 6 variant missions
Browse files Browse the repository at this point in the history
feedback and fixes

fixing logic for real this time

removing unnecessary import

SC2: race-swap logic fixes
  • Loading branch information
EnvyDragon committed Jul 2, 2024
1 parent cb19c52 commit ea64dac
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 79 deletions.
9 changes: 4 additions & 5 deletions worlds/sc2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
get_option_value, LocationInclusion, KerriganLevelItemDistribution,
KerriganPresence, KerriganPrimalStatus, kerrigan_unit_available, StarterUnit, SpearOfAdunPresence,
get_enabled_campaigns, SpearOfAdunAutonomouslyCastAbilityPresence, Starcraft2Options,
GrantStoryTech, GenericUpgradeResearch, GenericUpgradeItems, get_enabled_races
GrantStoryTech, GenericUpgradeResearch, GenericUpgradeItems,
)
from .pool_filter import filter_items
from .mission_tables import (
Expand Down Expand Up @@ -257,7 +257,6 @@ def resolve_count(count: Optional[int], max_count: int) -> int:
def flag_excludes_by_faction_presence(world: SC2World, item_list: List[FilterItem]) -> None:
"""Excludes items based on if their faction has a mission present where they can be used"""
missions = get_all_missions(world.mission_req_table)
races = get_enabled_races(world)
if world.options.take_over_ai_allies.value:
terran_missions = [mission for mission in missions if (MissionFlag.Terran|MissionFlag.AiTerranAlly) & mission.flags]
zerg_missions = [mission for mission in missions if (MissionFlag.Zerg|MissionFlag.AiZergAlly) & mission.flags]
Expand All @@ -276,13 +275,13 @@ def flag_excludes_by_faction_presence(world: SC2World, item_list: List[FilterIte

for item in item_list:
# Catch-all for all of a faction's items
if (not terran_missions or SC2Race.TERRAN not in races) and item.data.race == SC2Race.TERRAN:
if (not terran_missions and item.data.race == SC2Race.TERRAN):
item.flags |= ItemFilterFlags.Excluded
continue
if (not zerg_missions or SC2Race.ZERG not in races) and item.data.race == SC2Race.ZERG:
if (not zerg_missions and item.data.race == SC2Race.ZERG):
item.flags |= ItemFilterFlags.Excluded
continue
if (not protoss_missions or SC2Race.PROTOSS not in races) and item.data.race == SC2Race.PROTOSS:
if (not protoss_missions and item.data.race == SC2Race.PROTOSS):
if item.name not in item_groups.soa_items:
item.flags |= ItemFilterFlags.Excluded
continue
Expand Down
17 changes: 7 additions & 10 deletions worlds/sc2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1046,18 +1046,17 @@ def kerrigan_primal(ctx: SC2Context, kerrigan_level: int) -> bool:
return False


def get_mission_variant(mission_id):
def get_mission_variant(mission_id: int) -> int:
mission_flags = lookup_id_to_mission[mission_id].flags
faction_variant = 0
if MissionFlag.RaceSwap not in mission_flags:
return faction_variant
return 0
if MissionFlag.Terran in mission_flags:
faction_variant = 1
return 1
elif MissionFlag.Zerg in mission_flags:
faction_variant = 2
return 2
elif MissionFlag.Protoss in mission_flags:
faction_variant = 3
return faction_variant
return 3
return 0


async def starcraft_launch(ctx: SC2Context, mission_id: int):
Expand Down Expand Up @@ -1108,10 +1107,8 @@ async def on_step(self, iteration: int):
kerrigan_level = get_kerrigan_level(self.ctx, start_items, missions_beaten)
kerrigan_options = calculate_kerrigan_options(self.ctx)
soa_options = caclulate_soa_options(self.ctx)
mission_variant = get_mission_variant(self.mission_id)
mission_variant = get_mission_variant(self.mission_id) # 0/1/2/3 for unchanged/Terran/Zerg/Protoss
uncollected_objectives: typing.List[int] = self.get_uncollected_objectives()
# TODO: Add logic to determine which variant, based on selected map ID
mission_variant = 0 # 0/1/2/3 for unchanged/Terran/Zerg/Protoss
if self.ctx.difficulty_override >= 0:
difficulty = calc_difficulty(self.ctx.difficulty_override)
else:
Expand Down
143 changes: 106 additions & 37 deletions worlds/sc2/locations.py

Large diffs are not rendered by default.

19 changes: 14 additions & 5 deletions worlds/sc2/mission_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ def __init__(self, mission_id: int, name: str, campaign: SC2Campaign, area: str,

# Wings of Liberty
LIBERATION_DAY = 1, "Liberation Day", SC2Campaign.WOL, "Mar Sara", SC2Race.ANY, MissionPools.STARTER, "ap_liberation_day", MissionFlag.Terran|MissionFlag.NoBuild|MissionFlag.VsTerran
THE_OUTLAWS = 2, "The Outlaws", SC2Campaign.WOL, "Mar Sara", SC2Race.TERRAN, MissionPools.EASY, "ap_the_outlaws", MissionFlag.Terran|MissionFlag.VsTerran
ZERO_HOUR = 3, "Zero Hour", SC2Campaign.WOL, "Mar Sara", SC2Race.TERRAN, MissionPools.EASY, "ap_zero_hour", MissionFlag.Terran|MissionFlag.TimedDefense|MissionFlag.VsZerg
THE_OUTLAWS = 2, "The Outlaws (Terran)", SC2Campaign.WOL, "Mar Sara", SC2Race.TERRAN, MissionPools.EASY, "ap_the_outlaws", MissionFlag.Terran|MissionFlag.VsTerran|MissionFlag.HasRaceSwap
ZERO_HOUR = 3, "Zero Hour (Terran)", SC2Campaign.WOL, "Mar Sara", SC2Race.TERRAN, MissionPools.EASY, "ap_zero_hour", MissionFlag.Terran|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
EVACUATION = 4, "Evacuation", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.EASY, "ap_evacuation", MissionFlag.Terran|MissionFlag.AutoScroller|MissionFlag.VsZerg
OUTBREAK = 5, "Outbreak", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.EASY, "ap_outbreak", MissionFlag.Terran|MissionFlag.Defense|MissionFlag.VsZerg
SAFE_HAVEN = 6, "Safe Haven", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_safe_haven", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsProtoss
HAVENS_FALL = 7, "Haven's Fall", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_havens_fall", MissionFlag.Terran|MissionFlag.VsZerg
SMASH_AND_GRAB = 8, "Smash and Grab (T)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.EASY, "ap_smash_and_grab", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.HasRaceSwap
SMASH_AND_GRAB = 8, "Smash and Grab (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.EASY, "ap_smash_and_grab", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.HasRaceSwap
THE_DIG = 9, "The Dig", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_dig", MissionFlag.Terran|MissionFlag.TimedDefense|MissionFlag.VsProtoss
THE_MOEBIUS_FACTOR = 10, "The Moebius Factor", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_moebius_factor", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsZerg
SUPERNOVA = 11, "Supernova", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.HARD, "ap_supernova", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsProtoss
Expand Down Expand Up @@ -197,8 +197,17 @@ def __init__(self, mission_id: int, name: str, campaign: SC2Campaign, area: str,
END_GAME = 83, "End Game", SC2Campaign.NCO, "_3", SC2Race.TERRAN, MissionPools.VERY_HARD, "ap_end_game", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.Defense|MissionFlag.VsTerran

# Race-Swapped Variants
SMASH_AND_GRAB_Z = 98, "Smash and Grab (Z)", SC2Campaign.WOL, "Artifact", SC2Race.ZERG, MissionPools.EASY, "ap_smash_and_grab", MissionFlag.Zerg|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.RaceSwap
SMASH_AND_GRAB_P = 99, "Smash and Grab (P)", SC2Campaign.WOL, "Artifact", SC2Race.PROTOSS, MissionPools.EASY, "ap_smash_and_grab", MissionFlag.Protoss|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.RaceSwap
# 84/85 - Liberation Day
THE_OUTLAWS_Z = 86, "The Outlaws (Zerg)", SC2Campaign.WOL, "Mar Sara", SC2Race.ZERG, MissionPools.EASY, "ap_the_outlaws", MissionFlag.Zerg|MissionFlag.VsTerran|MissionFlag.RaceSwap
THE_OUTLAWS_P = 87, "The Outlaws (Protoss)", SC2Campaign.WOL, "Mar Sara", SC2Race.PROTOSS, MissionPools.EASY, "ap_the_outlaws", MissionFlag.Protoss|MissionFlag.VsTerran|MissionFlag.RaceSwap
ZERO_HOUR_Z = 88, "Zero Hour (Zerg)", SC2Campaign.WOL, "Mar Sara", SC2Race.ZERG, MissionPools.EASY, "ap_zero_hour", MissionFlag.Zerg|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.RaceSwap
ZERO_HOUR_P = 89, "Zero Hour (Protoss)", SC2Campaign.WOL, "Mar Sara", SC2Race.PROTOSS, MissionPools.EASY, "ap_zero_hour", MissionFlag.Protoss|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.RaceSwap
# 90/91 - Evacuation
# 92/93 - Outbreak
# 94/95 - Safe Haven
# 96/97 - Haven's Fall
SMASH_AND_GRAB_Z = 98, "Smash and Grab (Zerg)", SC2Campaign.WOL, "Artifact", SC2Race.ZERG, MissionPools.EASY, "ap_smash_and_grab", MissionFlag.Zerg|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.RaceSwap
SMASH_AND_GRAB_P = 99, "Smash and Grab (Protoss)", SC2Campaign.WOL, "Artifact", SC2Race.PROTOSS, MissionPools.EASY, "ap_smash_and_grab", MissionFlag.Protoss|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.RaceSwap


class MissionConnection:
Expand Down
13 changes: 6 additions & 7 deletions worlds/sc2/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class MaximumCampaignSize(Range):
"""
display_name = "Maximum Campaign Size"
range_start = 1
range_end = 85
range_end = 89
default = 83


Expand Down Expand Up @@ -263,15 +263,16 @@ class EnableNCOMissions(DefaultOnToggle):
class EnableRaceSwapVariants(Choice):
"""
Allow mission variants where you play a faction other than the one the map was initially
designed for.
designed for. NOTE: Cutscenes are always skipped on race-swapped mission variants.
Disabled: Don't shuffle any non-vanilla map variants into the pool.
Pick One: Only allow one version of each map at a time
Shuffle All: Each version of a map can appear in the same pool (so a map can appear up to 3 times as different races)
("Pick Just One At Random" coming soon)
"""
display_name = "Enable Race-Swapped Mission Variants"
option_disabled = 0
option_pick_one = 1
# TODO: Implement pick-one logic
# option_pick_one = 1
option_shuffle_all = 2
default = option_disabled

Expand Down Expand Up @@ -1012,7 +1013,7 @@ def get_enabled_campaigns(world: 'SC2World') -> Set[SC2Campaign]:
enabled_campaigns.add(SC2Campaign.EPILOGUE)
if get_option_value(world, "enable_nco_missions"):
enabled_campaigns.add(SC2Campaign.NCO)
return set([campaign for campaign in enabled_campaigns if campaign.race in get_enabled_races(world)])
return enabled_campaigns


def get_disabled_campaigns(world: 'SC2World') -> Set[SC2Campaign]:
Expand All @@ -1039,7 +1040,6 @@ def get_excluded_missions(world: 'SC2World') -> Set[SC2Mission]:
excluded_mission_names = world.options.excluded_missions.value
disabled_campaigns = get_disabled_campaigns(world)
disabled_flags = get_disabled_flags(world)
just_one_variant = get_option_value(world, "enable_race_swap") == EnableRaceSwapVariants.option_pick_one

excluded_missions: Set[SC2Mission] = set([lookup_name_to_mission[name] for name in excluded_mission_names])

Expand All @@ -1060,7 +1060,6 @@ def get_excluded_missions(world: 'SC2World') -> Set[SC2Mission]:
# Omitting missions not in enabled campaigns
for campaign in disabled_campaigns:
excluded_missions = excluded_missions.union(campaign_mission_table[campaign])
# TODO: if just_one_variant:

return excluded_missions

Expand Down
26 changes: 13 additions & 13 deletions worlds/sc2/regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,38 @@ def wol_cleared_missions(state: CollectionState, mission_count: int) -> bool:
player: int = world.player
if SC2Campaign.WOL in enabled_campaigns:
connect(world, names, 'Menu', 'Liberation Day')
connect(world, names, 'Liberation Day', 'The Outlaws',
connect(world, names, 'Liberation Day', 'The Outlaws (Terran)',
lambda state: state.has("Beat Liberation Day", player))
connect(world, names, 'The Outlaws', 'Zero Hour',
lambda state: state.has("Beat The Outlaws", player))
connect(world, names, 'Zero Hour', 'Evacuation',
lambda state: state.has("Beat Zero Hour", player))
connect(world, names, 'The Outlaws (Terran)', 'Zero Hour (Terran)',
lambda state: state.has("Beat The Outlaws (Terran)", player))
connect(world, names, 'Zero Hour (Terran)', 'Evacuation',
lambda state: state.has("Beat Zero Hour (Terran)", player))
connect(world, names, 'Evacuation', 'Outbreak',
lambda state: state.has("Beat Evacuation", player))
connect(world, names, "Outbreak", "Safe Haven",
lambda state: wol_cleared_missions(state, 7) and state.has("Beat Outbreak", player))
connect(world, names, "Outbreak", "Haven's Fall",
lambda state: wol_cleared_missions(state, 7) and state.has("Beat Outbreak", player))
connect(world, names, 'Zero Hour', 'Smash and Grab',
lambda state: state.has("Beat Zero Hour", player))
connect(world, names, 'Smash and Grab', 'The Dig',
lambda state: wol_cleared_missions(state, 8) and state.has("Beat Smash and Grab", player))
connect(world, names, 'Zero Hour (Terran)', 'Smash and Grab (Terran)',
lambda state: state.has("Beat Zero Hour (Terran)", player))
connect(world, names, 'Smash and Grab (Terran)', 'The Dig',
lambda state: wol_cleared_missions(state, 8) and state.has("Beat Smash and Grab (Terran)", player))
connect(world, names, 'The Dig', 'The Moebius Factor',
lambda state: wol_cleared_missions(state, 11) and state.has("Beat The Dig", player))
connect(world, names, 'The Moebius Factor', 'Supernova',
lambda state: wol_cleared_missions(state, 14) and state.has("Beat The Moebius Factor", player))
connect(world, names, 'Supernova', 'Maw of the Void',
lambda state: state.has("Beat Supernova", player))
connect(world, names, 'Zero Hour', "Devil's Playground",
lambda state: wol_cleared_missions(state, 4) and state.has("Beat Zero Hour", player))
connect(world, names, 'Zero Hour (Terran)', "Devil's Playground",
lambda state: wol_cleared_missions(state, 4) and state.has("Beat Zero Hour (Terran)", player))
connect(world, names, "Devil's Playground", 'Welcome to the Jungle',
lambda state: state.has("Beat Devil's Playground", player))
connect(world, names, "Welcome to the Jungle", 'Breakout',
lambda state: wol_cleared_missions(state, 8) and state.has("Beat Welcome to the Jungle", player))
connect(world, names, "Welcome to the Jungle", 'Ghost of a Chance',
lambda state: wol_cleared_missions(state, 8) and state.has("Beat Welcome to the Jungle", player))
connect(world, names, "Zero Hour", 'The Great Train Robbery',
lambda state: wol_cleared_missions(state, 6) and state.has("Beat Zero Hour", player))
connect(world, names, "Zero Hour (Terran)", 'The Great Train Robbery',
lambda state: wol_cleared_missions(state, 6) and state.has("Beat Zero Hour (Terran)", player))
connect(world, names, 'The Great Train Robbery', 'Cutthroat',
lambda state: state.has("Beat The Great Train Robbery", player))
connect(world, names, 'Cutthroat', 'Engine of Destruction',
Expand Down
2 changes: 1 addition & 1 deletion worlds/sc2/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,7 @@ def __init__(self, world: 'SC2World'):
self.advanced_tactics = self.logic_level != RequiredTactics.option_standard
self.take_over_ai_allies = get_option_value(world, "take_over_ai_allies") == TakeOverAIAllies.option_true
self.kerrigan_unit_available = get_option_value(world, 'kerrigan_presence') in kerrigan_unit_available \
and SC2Campaign.HOTS in get_enabled_campaigns(world)
and SC2Campaign.HOTS in get_enabled_campaigns(world) and SC2Race.ZERG in get_enabled_races(world)
self.kerrigan_levels_per_mission_completed = get_option_value(world, "kerrigan_levels_per_mission_completed")
self.kerrigan_levels_per_mission_completed_cap = get_option_value(world, "kerrigan_levels_per_mission_completed_cap")
self.kerrigan_total_level_cap = get_option_value(world, "kerrigan_total_level_cap")
Expand Down
3 changes: 2 additions & 1 deletion worlds/sc2/test/test_usecases.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ def test_excluding_zerg_excludes_campaigns_and_items(self) -> None:
for item_name in world_item_names:
self.assertNotEqual(items.item_table[item_name].race, mission_tables.SC2Race.ZERG,
f"{item_name} is a ZERG item!")
for region in world_regions:
# have to manually exclude the only non-zerg HotS mission...
for region in filter(lambda region: region != "With Friends Like These", world_regions):
self.assertNotIn(mission_tables.lookup_name_to_mission[region].campaign,
([mission_tables.SC2Campaign.HOTS]),
f"{region} is a ZERG mission!")
Expand Down

0 comments on commit ea64dac

Please sign in to comment.