From 0427a922fbc43705baecb2279960af3bafe30812 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sat, 4 May 2024 16:10:40 -0400 Subject: [PATCH 01/24] Reimplemented golden path order Signed-off-by: Magnemania --- worlds/sc2/MissionTables.py | 4 +- worlds/sc2/Options.py | 5 ++ worlds/sc2/PoolFilter.py | 4 +- worlds/sc2/Regions.py | 91 +++++++++++++++++++++++++++++++++++-- 4 files changed, 97 insertions(+), 7 deletions(-) diff --git a/worlds/sc2/MissionTables.py b/worlds/sc2/MissionTables.py index 7394a928842e..adaf7c1e61da 100644 --- a/worlds/sc2/MissionTables.py +++ b/worlds/sc2/MissionTables.py @@ -217,7 +217,7 @@ class MissionInfo(NamedTuple): number: int = 0 # number of worlds need beaten completion_critical: bool = False # missions needed to beat game or_requirements: bool = False # true if the requirements should be or-ed instead of and-ed - ui_vertical_padding: int = 0 + ui_vertical_padding: int = 0 # How many blank padding tiles go above this mission in the launcher class FillMission(NamedTuple): @@ -228,7 +228,7 @@ class FillMission(NamedTuple): completion_critical: bool = False # missions needed to beat game or_requirements: bool = False # true if the requirements should be or-ed instead of and-ed removal_priority: int = 0 # how many missions missing from the pool required to remove this mission - + ui_vertical_padding: int = 0 # How many blank padding tiles go above this mission in the launcher def vanilla_shuffle_order() -> Dict[SC2Campaign, List[FillMission]]: diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index 5e73ca58ed6a..008008dde58b 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -80,6 +80,7 @@ class MissionOrder(Choice): option_mini_gauntlet = 7 option_tiny_grid = 8 option_grid = 9 + option_golden_path = 10 class MaximumCampaignSize(Range): @@ -904,6 +905,10 @@ def get_excluded_missions(world: World) -> Set[SC2Mission]: MissionOrder.option_mini_campaign ] +dynamic_mission_orders = [ + MissionOrder.option_golden_path +] + kerrigan_unit_available = [ KerriganPresence.option_vanilla, ] \ No newline at end of file diff --git a/worlds/sc2/PoolFilter.py b/worlds/sc2/PoolFilter.py index e94dc4e214c8..95a4b5ab1209 100644 --- a/worlds/sc2/PoolFilter.py +++ b/worlds/sc2/PoolFilter.py @@ -8,7 +8,7 @@ from .Options import get_option_value, MissionOrder, \ get_enabled_campaigns, get_disabled_campaigns, RequiredTactics, kerrigan_unit_available, GrantStoryTech, \ TakeOverAIAllies, SpearOfAdunPresence, SpearOfAdunAutonomouslyCastAbilityPresence, campaign_depending_orders, \ - ShuffleCampaigns, get_excluded_missions, ShuffleNoBuild, ExtraLocations, GrantStoryLevels + ShuffleCampaigns, get_excluded_missions, ShuffleNoBuild, ExtraLocations, GrantStoryLevels, dynamic_mission_orders from . import ItemNames from worlds.AutoWorld import World @@ -208,7 +208,7 @@ def copy_item(item: Item): def num_missions(world: World) -> int: mission_order_type = get_option_value(world, "mission_order") - if mission_order_type != MissionOrder.option_grid: + if mission_order_type not in dynamic_mission_orders: mission_order = mission_orders[mission_order_type]() misssions = [mission for campaign in mission_order for mission in mission_order[campaign]] return len(misssions) - 1 # Menu diff --git a/worlds/sc2/Regions.py b/worlds/sc2/Regions.py index 84830a9a32bd..b25d788c2b3e 100644 --- a/worlds/sc2/Regions.py +++ b/worlds/sc2/Regions.py @@ -6,7 +6,7 @@ from .Options import get_option_value, MissionOrder, get_enabled_campaigns, campaign_depending_orders, \ GridTwoStartPositions from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, \ - MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection + MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection, FillMission from .PoolFilter import filter_missions from worlds.AutoWorld import World @@ -375,6 +375,86 @@ def make_grid_connect_rule( return lambda state: state.has(f"Beat {missions[connected_coords].mission_name}", player) +def make_dynamic_mission_order( + world: World, + locations: Tuple[LocationData, ...], + location_cache: List[Location], + mission_order_type: int +) -> Tuple[Dict[SC2Campaign, Dict[str, MissionInfo]], int, str]: + player = world.player + mission_pools = filter_missions(world) + + mission_pool = [mission for mission_pool in mission_pools.values() for mission in mission_pool] + + num_missions = min(len(mission_pool), get_option_value(world, "maximum_campaign_size")) + num_missions = max(2, num_missions) + chain_name_options = ['Mar Sara', 'Char', 'Kaldir', 'Zerus', 'Skygeirr Station', + 'Dominion Space', 'Korhal', 'Aiur', 'Shakuras', 'Ulnar'] + world.random.shuffle(chain_name_options) + + mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] + first_chain = chain_name_options.pop() + first_mission = FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.GLOBAL)], first_chain, + completion_critical=True) + + class Campaign: + def __init__(self, first_mission: FillMission, missions_remaining: int): + self.mission_order = [first_mission] + self.counter = 0 + self.last_mission_in_chain = [0] + self.chain_names = [first_mission.category] + self.missions_remaining = missions_remaining + self.padding = 0 + + def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): + if self.missions_remaining == 0 and difficulty is not MissionPools.FINAL: + return + self.counter += 1 + if self.mission_order[self.last_mission_in_chain[chain]].number == required_missions or required_missions <= 1: + required_missions = 0 + mission_connections = [MissionConnection(self.last_mission_in_chain[chain], SC2Campaign.GLOBAL)] + padding = 0 + if self.last_mission_in_chain[chain] == self.last_mission_in_chain[0]: + # Adding padding to the start of new chains + if chain != 0: + padding = self.padding + else: + # Requiring main chain progress for optional chains + mission_connections.append(MissionConnection(self.last_mission_in_chain[0], SC2Campaign.GLOBAL)) + self.mission_order.append(FillMission( + difficulty, + mission_connections, + self.chain_names[chain], + number=required_missions, + completion_critical=chain == 0, + ui_vertical_padding=padding + )) + self.last_mission_in_chain[chain] = self.counter + if chain == 0: + self.padding += 1 + self.missions_remaining -= 1 + + campaign = Campaign(first_mission, num_missions - 2) + current_required_missions = 0 + main_chain = 0 + while campaign.missions_remaining > 0: + main_chain += 1 + mission_difficulty = mission_difficulties[min(main_chain // 2, 3)] + if main_chain % 2 == 1: # Adding branches + chains_to_make = 0 if len(campaign.chain_names) > 5 else 2 if main_chain == 1 else 1 + for new_chain in range(chains_to_make): + campaign.chain_names.append(chain_name_options.pop()) + campaign.last_mission_in_chain.append(campaign.last_mission_in_chain[0]) + # Updating branches + for side_chain in range(len(campaign.chain_names) - 1, 0, -1): + campaign.add_mission(side_chain, mission_difficulty) + # Adding main path mission + current_required_missions = (len(campaign.mission_order) * 3) // 4 + campaign.add_mission(0, mission_difficulty, current_required_missions) + campaign.add_mission(0, MissionPools.FINAL, current_required_missions) + return {SC2Campaign.GLOBAL: campaign.mission_order} + + def create_structured_regions( world: World, locations: Tuple[LocationData, ...], @@ -383,7 +463,10 @@ def create_structured_regions( ) -> Tuple[Dict[SC2Campaign, Dict[str, MissionInfo]], int, str]: locations_per_region = get_locations_per_region(locations) - mission_order = mission_orders[mission_order_type]() + if mission_order_type == MissionOrder.option_golden_path: + mission_order = make_dynamic_mission_order(world, locations, location_cache, mission_order_type) + else: + mission_order = mission_orders[mission_order_type]() enabled_campaigns = get_enabled_campaigns(world) shuffle_campaigns = get_option_value(world, "shuffle_campaigns") @@ -592,7 +675,9 @@ def build_connection_rule(mission_names: List[str], missions_req: int) -> Callab mission.slot, connections, mission_order[campaign][i].category, number=mission_order[campaign][i].number, completion_critical=mission_order[campaign][i].completion_critical, - or_requirements=mission_order[campaign][i].or_requirements)}) + or_requirements=mission_order[campaign][i].or_requirements, + ui_vertical_padding=mission_order[campaign][i].ui_vertical_padding), + }) final_mission_id = final_mission.id # Changing the completion condition for alternate final missions into an event From 2b7e62e0eee5e23b981d236cb148418e9039e0bd Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sat, 4 May 2024 18:50:51 -0400 Subject: [PATCH 02/24] Added dynamic gauntlet Signed-off-by: Magnemania --- worlds/sc2/MissionTables.py | 27 -------------------- worlds/sc2/Options.py | 4 ++- worlds/sc2/Regions.py | 49 +++++++++++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 30 deletions(-) diff --git a/worlds/sc2/MissionTables.py b/worlds/sc2/MissionTables.py index adaf7c1e61da..449b933e896c 100644 --- a/worlds/sc2/MissionTables.py +++ b/worlds/sc2/MissionTables.py @@ -399,31 +399,6 @@ def mini_campaign_order() -> Dict[SC2Campaign, List[FillMission]]: } -def gauntlet_order() -> Dict[SC2Campaign, List[FillMission]]: - return { - SC2Campaign.GLOBAL: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1)], "I", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(0)], "II", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(1)], "III", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(2)], "IV", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(3)], "V", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(4)], "VI", completion_critical=True), - FillMission(MissionPools.FINAL, [MissionConnection(5)], "Final", completion_critical=True) - ] - } - - -def mini_gauntlet_order() -> Dict[SC2Campaign, List[FillMission]]: - return { - SC2Campaign.GLOBAL: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1)], "I", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(0)], "II", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(1)], "III", completion_critical=True), - FillMission(MissionPools.FINAL, [MissionConnection(2)], "Final", completion_critical=True) - ] - } - - def grid_order() -> Dict[SC2Campaign, List[FillMission]]: return { SC2Campaign.GLOBAL: [ @@ -497,8 +472,6 @@ def blitz_order() -> Dict[SC2Campaign, List[FillMission]]: grid_order, mini_grid_order, blitz_order, - gauntlet_order, - mini_gauntlet_order, tiny_grid_order ] diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index 008008dde58b..aa13b3de568f 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -906,7 +906,9 @@ def get_excluded_missions(world: World) -> Set[SC2Mission]: ] dynamic_mission_orders = [ - MissionOrder.option_golden_path + MissionOrder.option_golden_path, + MissionOrder.option_grid, + MissionOrder.option_gauntlet ] kerrigan_unit_available = [ diff --git a/worlds/sc2/Regions.py b/worlds/sc2/Regions.py index b25d788c2b3e..016b85ee19aa 100644 --- a/worlds/sc2/Regions.py +++ b/worlds/sc2/Regions.py @@ -4,7 +4,7 @@ from BaseClasses import MultiWorld, Region, Entrance, Location, CollectionState from .Locations import LocationData from .Options import get_option_value, MissionOrder, get_enabled_campaigns, campaign_depending_orders, \ - GridTwoStartPositions + GridTwoStartPositions, dynamic_mission_orders from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, \ MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection, FillMission from .PoolFilter import filter_missions @@ -388,6 +388,15 @@ def make_dynamic_mission_order( num_missions = min(len(mission_pool), get_option_value(world, "maximum_campaign_size")) num_missions = max(2, num_missions) + if mission_order_type == MissionOrder.option_golden_path: + return make_golden_path(world, num_missions) + # Grid handled by dedicated region generator + elif mission_order_type == MissionOrder.option_gauntlet: + return make_gauntlet(num_missions) + + + +def make_golden_path(world, num_missions): chain_name_options = ['Mar Sara', 'Char', 'Kaldir', 'Zerus', 'Skygeirr Station', 'Dominion Space', 'Korhal', 'Aiur', 'Shakuras', 'Ulnar'] world.random.shuffle(chain_name_options) @@ -455,6 +464,42 @@ def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): return {SC2Campaign.GLOBAL: campaign.mission_order} +def make_gauntlet(num_missions): + mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] + mission_order: list[FillMission] = [] + row_length = 7 + rows = math.ceil(num_missions / row_length) + if rows == 1: + column_names = ["I", "II", "III", "IV", "V", "VI", "VII"][:num_missions - 1] + column_names.append("Final") + else: + column_names = [f'_{col + 1}' for col in range(row_length)] + first_mission = FillMission(MissionPools.STARTER, [MissionConnection(-1)], column_names[0], + completion_critical=True) + mission_order.append(first_mission) + mission_number = 1 + space_rows = 0 + while mission_number < num_missions: + if mission_number == num_missions - 1: + difficulty = MissionPools.FINAL + else: + difficulty_progress = math.floor(min(mission_number * 4 / num_missions, mission_number / 3)) + difficulty = mission_difficulties[min(difficulty_progress, 3)] + connection = MissionConnection(mission_number - 1) + mission_order.append(FillMission( + difficulty, + [connection], + column_names[mission_number % row_length], + completion_critical=True, + ui_vertical_padding=space_rows + )) + if mission_number == row_length - 1: + space_rows += 1 + mission_number += 1 + return {SC2Campaign.GLOBAL: mission_order} + + + def create_structured_regions( world: World, locations: Tuple[LocationData, ...], @@ -463,7 +508,7 @@ def create_structured_regions( ) -> Tuple[Dict[SC2Campaign, Dict[str, MissionInfo]], int, str]: locations_per_region = get_locations_per_region(locations) - if mission_order_type == MissionOrder.option_golden_path: + if mission_order_type in dynamic_mission_orders: mission_order = make_dynamic_mission_order(world, locations, location_cache, mission_order_type) else: mission_order = mission_orders[mission_order_type]() From ff2db996f20cb2f73a42dc3369026b5ddc4f8854 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sat, 4 May 2024 22:07:30 -0400 Subject: [PATCH 03/24] Reworked Blitz (now vertical, uses or requirements, dynamic) Signed-off-by: Magnemania --- worlds/sc2/Client.py | 4 +-- worlds/sc2/MissionTables.py | 19 -------------- worlds/sc2/Options.py | 3 ++- worlds/sc2/Regions.py | 50 ++++++++++++++++++++++++++++++++----- 4 files changed, 48 insertions(+), 28 deletions(-) diff --git a/worlds/sc2/Client.py b/worlds/sc2/Client.py index 78ce7902a5c7..7490c36971c6 100644 --- a/worlds/sc2/Client.py +++ b/worlds/sc2/Client.py @@ -1364,8 +1364,8 @@ def mission_reqs_completed(ctx: SC2Context, mission_name: str, missions_complete else: req_success = False - # Grid-specific logic (to avoid long path checks and infinite recursion) - if ctx.mission_order in (MissionOrder.option_grid, MissionOrder.option_mini_grid, MissionOrder.option_medium_grid): + # Grid and Blitz-specific logic (to avoid long path checks and infinite recursion) (Includes legacy grids) + if ctx.mission_order in (MissionOrder.option_grid, MissionOrder.option_blitz, 3, 4, 8): if req_success: return True else: diff --git a/worlds/sc2/MissionTables.py b/worlds/sc2/MissionTables.py index 449b933e896c..3e09921af481 100644 --- a/worlds/sc2/MissionTables.py +++ b/worlds/sc2/MissionTables.py @@ -446,24 +446,6 @@ def tiny_grid_order() -> Dict[SC2Campaign, List[FillMission]]: ] } -def blitz_order() -> Dict[SC2Campaign, List[FillMission]]: - return { - SC2Campaign.GLOBAL: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1)], "I"), - FillMission(MissionPools.EASY, [MissionConnection(-1)], "I"), - FillMission(MissionPools.MEDIUM, [MissionConnection(0), MissionConnection(1)], "II", number=1, or_requirements=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(0), MissionConnection(1)], "II", number=1, or_requirements=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(0), MissionConnection(1)], "III", number=2, or_requirements=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(0), MissionConnection(1)], "III", number=2, or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(0), MissionConnection(1)], "IV", number=3, or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(0), MissionConnection(1)], "IV", number=3, or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(0), MissionConnection(1)], "V", number=4, or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(0), MissionConnection(1)], "V", number=4, or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(0), MissionConnection(1)], "Final", number=5, or_requirements=True), - FillMission(MissionPools.FINAL, [MissionConnection(0), MissionConnection(1)], "Final", number=5, or_requirements=True) - ] - } - mission_orders: List[Callable[[], Dict[SC2Campaign, List[FillMission]]]] = [ vanilla_shuffle_order, @@ -471,7 +453,6 @@ def blitz_order() -> Dict[SC2Campaign, List[FillMission]]: mini_campaign_order, grid_order, mini_grid_order, - blitz_order, tiny_grid_order ] diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index aa13b3de568f..66d35f92cbcf 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -908,7 +908,8 @@ def get_excluded_missions(world: World) -> Set[SC2Mission]: dynamic_mission_orders = [ MissionOrder.option_golden_path, MissionOrder.option_grid, - MissionOrder.option_gauntlet + MissionOrder.option_gauntlet, + MissionOrder.option_blitz ] kerrigan_unit_available = [ diff --git a/worlds/sc2/Regions.py b/worlds/sc2/Regions.py index 016b85ee19aa..c3d5ba151db9 100644 --- a/worlds/sc2/Regions.py +++ b/worlds/sc2/Regions.py @@ -393,10 +393,11 @@ def make_dynamic_mission_order( # Grid handled by dedicated region generator elif mission_order_type == MissionOrder.option_gauntlet: return make_gauntlet(num_missions) + elif mission_order_type == MissionOrder.option_blitz: + return make_blitz(num_missions) - -def make_golden_path(world, num_missions): +def make_golden_path(world, num_missions) -> list[FillMission]: chain_name_options = ['Mar Sara', 'Char', 'Kaldir', 'Zerus', 'Skygeirr Station', 'Dominion Space', 'Korhal', 'Aiur', 'Shakuras', 'Ulnar'] world.random.shuffle(chain_name_options) @@ -464,7 +465,7 @@ def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): return {SC2Campaign.GLOBAL: campaign.mission_order} -def make_gauntlet(num_missions): +def make_gauntlet(num_missions) -> list[FillMission]: mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] mission_order: list[FillMission] = [] row_length = 7 @@ -475,7 +476,7 @@ def make_gauntlet(num_missions): else: column_names = [f'_{col + 1}' for col in range(row_length)] first_mission = FillMission(MissionPools.STARTER, [MissionConnection(-1)], column_names[0], - completion_critical=True) + completion_critical=True) mission_order.append(first_mission) mission_number = 1 space_rows = 0 @@ -493,12 +494,49 @@ def make_gauntlet(num_missions): completion_critical=True, ui_vertical_padding=space_rows )) - if mission_number == row_length - 1: - space_rows += 1 mission_number += 1 + # Next row + if mission_number == row_length: + space_rows += 1 return {SC2Campaign.GLOBAL: mission_order} +def make_blitz(num_missions) -> list[FillMission]: + mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] + min_width, max_width = 2, 5 + mission_divisor = 5 + dynamic_width = num_missions / mission_divisor + width = max(min(dynamic_width, max_width), min_width) + middle_column = math.floor(width / 2) + connections = [MissionConnection(-1)] + mission_number = 0 + mission_order: List[FillMission] = [] + if num_missions % width > middle_column: + final_row = math.floor(num_missions / width) * width + final_mission_number = final_row + middle_column + else: + final_mission_number = num_missions - 1 + while mission_number < num_missions: + column = mission_number % width + if mission_number == middle_column: + difficulty = MissionPools.STARTER + elif mission_number == final_mission_number: + difficulty = MissionPools.FINAL + else: + difficulty = mission_difficulties[math.floor(min(mission_number / width, 3))] + mission_order.append(FillMission( + difficulty, + connections, + f'_{column}', + or_requirements=True + )) + mission_number += 1 + # Next row, requires previous row + if mission_number % width == 0: + connections = [MissionConnection(mission_number - 1 - i) for i in range(width)] + connections.reverse() + return {SC2Campaign.GLOBAL: mission_order} + def create_structured_regions( world: World, From 1642fbf184b503e76081f82bab985baa90f27abf Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sun, 5 May 2024 08:43:37 -0400 Subject: [PATCH 04/24] Removed deprecated grid/gauntlet alternatives and cleaned up static/dynamic distinction Signed-off-by: Magnemania --- worlds/sc2/MissionTables.py | 58 ------------------------------------- worlds/sc2/Options.py | 25 ++++++++-------- worlds/sc2/PoolFilter.py | 9 +++--- worlds/sc2/Regions.py | 16 +++++----- 4 files changed, 24 insertions(+), 84 deletions(-) diff --git a/worlds/sc2/MissionTables.py b/worlds/sc2/MissionTables.py index 3e09921af481..cf7acace8d4a 100644 --- a/worlds/sc2/MissionTables.py +++ b/worlds/sc2/MissionTables.py @@ -399,64 +399,6 @@ def mini_campaign_order() -> Dict[SC2Campaign, List[FillMission]]: } -def grid_order() -> Dict[SC2Campaign, List[FillMission]]: - return { - SC2Campaign.GLOBAL: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1)], "_1"), - FillMission(MissionPools.EASY, [MissionConnection(0)], "_1"), - FillMission(MissionPools.MEDIUM, [MissionConnection(1), MissionConnection(6), MissionConnection( 3)], "_1", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(2), MissionConnection(7)], "_1", or_requirements=True), - FillMission(MissionPools.EASY, [MissionConnection(0)], "_2"), - FillMission(MissionPools.MEDIUM, [MissionConnection(1), MissionConnection(4)], "_2", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(2), MissionConnection(5), MissionConnection(10), MissionConnection(7)], "_2", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(3), MissionConnection(6), MissionConnection(11)], "_2", or_requirements=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(4), MissionConnection(9), MissionConnection(12)], "_3", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(5), MissionConnection(8), MissionConnection(10), MissionConnection(13)], "_3", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(6), MissionConnection(9), MissionConnection(11), MissionConnection(14)], "_3", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(7), MissionConnection(10)], "_3", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(8), MissionConnection(13)], "_4", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(9), MissionConnection(12), MissionConnection(14)], "_4", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(10), MissionConnection(13)], "_4", or_requirements=True), - FillMission(MissionPools.FINAL, [MissionConnection(11), MissionConnection(14)], "_4", or_requirements=True) - ] - } - -def mini_grid_order() -> Dict[SC2Campaign, List[FillMission]]: - return { - SC2Campaign.GLOBAL: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1)], "_1"), - FillMission(MissionPools.EASY, [MissionConnection(0)], "_1"), - FillMission(MissionPools.MEDIUM, [MissionConnection(1), MissionConnection(5)], "_1", or_requirements=True), - FillMission(MissionPools.EASY, [MissionConnection(0)], "_2"), - FillMission(MissionPools.MEDIUM, [MissionConnection(1), MissionConnection(3)], "_2", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(2), MissionConnection(4)], "_2", or_requirements=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(3), MissionConnection(7)], "_3", or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(4), MissionConnection(6)], "_3", or_requirements=True), - FillMission(MissionPools.FINAL, [MissionConnection(5), MissionConnection(7)], "_3", or_requirements=True) - ] - } - -def tiny_grid_order() -> Dict[SC2Campaign, List[FillMission]]: - return { - SC2Campaign.GLOBAL: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1)], "_1"), - FillMission(MissionPools.MEDIUM, [MissionConnection(0)], "_1"), - FillMission(MissionPools.EASY, [MissionConnection(0)], "_2"), - FillMission(MissionPools.FINAL, [MissionConnection(1), MissionConnection(2)], "_2", or_requirements=True), - ] - } - - -mission_orders: List[Callable[[], Dict[SC2Campaign, List[FillMission]]]] = [ - vanilla_shuffle_order, - vanilla_shuffle_order, - mini_campaign_order, - grid_order, - mini_grid_order, - tiny_grid_order -] - - vanilla_mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]] = { SC2Campaign.WOL: { SC2Mission.LIBERATION_DAY.mission_name: MissionInfo(SC2Mission.LIBERATION_DAY, [], SC2Mission.LIBERATION_DAY.area, completion_critical=True), diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index 66d35f92cbcf..e1971f2aea97 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -3,7 +3,7 @@ from Options import Choice, Toggle, DefaultOnToggle, ItemSet, OptionSet, Range, PerGameCommonOptions from .MissionTables import SC2Campaign, SC2Mission, lookup_name_to_mission, MissionPools, get_no_build_missions, \ - campaign_mission_table + campaign_mission_table, vanilla_shuffle_order, mini_campaign_order from worlds.AutoWorld import World @@ -57,28 +57,21 @@ class AllInMap(Choice): class MissionOrder(Choice): """ - Determines the order the missions are played in. The last three mission orders end in a random mission. + Determines the order the missions are played in. The first three mission orders ignore the Maximum Campaign Size option. Vanilla (83 total if all campaigns enabled): Keeps the standard mission order and branching from the vanilla Campaigns. Vanilla Shuffled (83 total if all campaigns enabled): Keeps same branching paths from the vanilla Campaigns but randomizes the order of missions within. Mini Campaign (47 total if all campaigns enabled): Shorter version of the campaign with randomized missions and optional branches. - Medium Grid (16): A 4x4 grid of random missions. Start at the top-left and forge a path towards bottom-right mission to win. - Mini Grid (9): A 3x3 version of Grid. Complete the bottom-right mission to win. - Blitz (12): 12 random missions that open up very quickly. Complete the bottom-right mission to win. - Gauntlet (7): Linear series of 7 random missions to complete the campaign. - Mini Gauntlet (4): Linear series of 4 random missions to complete the campaign. - Tiny Grid (4): A 2x2 version of Grid. Complete the bottom-right mission to win. - Grid (variable): A grid that will resize to use all non-excluded missions. Corners may be omitted to make the grid more square. Complete the bottom-right mission to win. + Blitz: Missions are divided into sets. Complete one mission from a set to advance to the next set. + Gauntlet: A linear path of missions to complete the campaign. + Grid: A grid that will resize to use all non-excluded missions. Corners may be omitted to make the grid more square. Complete the bottom-right mission to win. + Golden Path: """ display_name = "Mission Order" option_vanilla = 0 option_vanilla_shuffled = 1 option_mini_campaign = 2 - option_medium_grid = 3 - option_mini_grid = 4 option_blitz = 5 option_gauntlet = 6 - option_mini_gauntlet = 7 - option_tiny_grid = 8 option_grid = 9 option_golden_path = 10 @@ -905,6 +898,12 @@ def get_excluded_missions(world: World) -> Set[SC2Mission]: MissionOrder.option_mini_campaign ] +static_mission_orders = { + MissionOrder.option_vanilla: vanilla_shuffle_order, + MissionOrder.option_vanilla_shuffled: vanilla_shuffle_order, + MissionOrder.option_mini_campaign: mini_campaign_order +} + dynamic_mission_orders = [ MissionOrder.option_golden_path, MissionOrder.option_grid, diff --git a/worlds/sc2/PoolFilter.py b/worlds/sc2/PoolFilter.py index 95a4b5ab1209..5986e7adf065 100644 --- a/worlds/sc2/PoolFilter.py +++ b/worlds/sc2/PoolFilter.py @@ -2,13 +2,14 @@ from BaseClasses import Item, Location from .Items import get_full_item_list, spider_mine_sources, second_pass_placeable_items, progressive_if_nco, \ progressive_if_ext, spear_of_adun_calldowns, spear_of_adun_castable_passives, nova_equipment -from .MissionTables import mission_orders, MissionInfo, MissionPools, \ +from .MissionTables import MissionInfo, MissionPools, \ get_campaign_goal_priority, campaign_final_mission_locations, campaign_alt_final_mission_locations, \ SC2Campaign, SC2Race, SC2CampaignGoalPriority, SC2Mission from .Options import get_option_value, MissionOrder, \ get_enabled_campaigns, get_disabled_campaigns, RequiredTactics, kerrigan_unit_available, GrantStoryTech, \ TakeOverAIAllies, SpearOfAdunPresence, SpearOfAdunAutonomouslyCastAbilityPresence, campaign_depending_orders, \ - ShuffleCampaigns, get_excluded_missions, ShuffleNoBuild, ExtraLocations, GrantStoryLevels, dynamic_mission_orders + ShuffleCampaigns, get_excluded_missions, ShuffleNoBuild, ExtraLocations, GrantStoryLevels, \ + static_mission_orders, dynamic_mission_orders from . import ItemNames from worlds.AutoWorld import World @@ -208,8 +209,8 @@ def copy_item(item: Item): def num_missions(world: World) -> int: mission_order_type = get_option_value(world, "mission_order") - if mission_order_type not in dynamic_mission_orders: - mission_order = mission_orders[mission_order_type]() + if mission_order_type in static_mission_orders: + mission_order = static_mission_orders[mission_order_type]() misssions = [mission for campaign in mission_order for mission in mission_order[campaign]] return len(misssions) - 1 # Menu else: diff --git a/worlds/sc2/Regions.py b/worlds/sc2/Regions.py index c3d5ba151db9..37f1115dcfd0 100644 --- a/worlds/sc2/Regions.py +++ b/worlds/sc2/Regions.py @@ -4,8 +4,8 @@ from BaseClasses import MultiWorld, Region, Entrance, Location, CollectionState from .Locations import LocationData from .Options import get_option_value, MissionOrder, get_enabled_campaigns, campaign_depending_orders, \ - GridTwoStartPositions, dynamic_mission_orders -from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, \ + GridTwoStartPositions, static_mission_orders, dynamic_mission_orders +from .MissionTables import MissionInfo, vanilla_mission_req_table, \ MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection, FillMission from .PoolFilter import filter_missions from worlds.AutoWorld import World @@ -26,7 +26,7 @@ def create_regions( * int The number of missions in the world * str The name of the goal location """ - mission_order_type: int = get_option_value(world, "mission_order") + mission_order_type: MissionOrder = get_option_value(world, "mission_order") if mission_order_type == MissionOrder.option_vanilla: return create_vanilla_regions(world, locations, location_cache) @@ -377,8 +377,6 @@ def make_grid_connect_rule( def make_dynamic_mission_order( world: World, - locations: Tuple[LocationData, ...], - location_cache: List[Location], mission_order_type: int ) -> Tuple[Dict[SC2Campaign, Dict[str, MissionInfo]], int, str]: player = world.player @@ -542,14 +540,14 @@ def create_structured_regions( world: World, locations: Tuple[LocationData, ...], location_cache: List[Location], - mission_order_type: int, + mission_order_type: MissionOrder, ) -> Tuple[Dict[SC2Campaign, Dict[str, MissionInfo]], int, str]: locations_per_region = get_locations_per_region(locations) - if mission_order_type in dynamic_mission_orders: - mission_order = make_dynamic_mission_order(world, locations, location_cache, mission_order_type) + if mission_order_type in static_mission_orders: + mission_order = static_mission_orders[mission_order_type]() else: - mission_order = mission_orders[mission_order_type]() + mission_order = make_dynamic_mission_order(world, mission_order_type) enabled_campaigns = get_enabled_campaigns(world) shuffle_campaigns = get_option_value(world, "shuffle_campaigns") From 72e2e2dc90a8a8027cbe656500a6f84655fdf8fa Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sun, 5 May 2024 09:09:23 -0400 Subject: [PATCH 05/24] Relocated mission orders to MissionOrders.py Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 314 ++++++++++++++++++++++++++++++++++++ worlds/sc2/MissionTables.py | 168 ------------------- worlds/sc2/Options.py | 3 +- worlds/sc2/Regions.py | 144 +---------------- 4 files changed, 318 insertions(+), 311 deletions(-) create mode 100644 worlds/sc2/MissionOrders.py diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py new file mode 100644 index 000000000000..c9a204d8154f --- /dev/null +++ b/worlds/sc2/MissionOrders.py @@ -0,0 +1,314 @@ +from .MissionTables import FillMission, MissionPools, MissionConnection, SC2Campaign +from typing import Dict, List +import math + +from worlds.AutoWorld import World + + +def vanilla_shuffle_order() -> Dict[SC2Campaign, List[FillMission]]: + return { + SC2Campaign.WOL: [ + FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.WOL)], "Mar Sara", completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.WOL)], "Mar Sara", completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.WOL)], "Mar Sara", completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.WOL)], "Colonist"), + FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.WOL)], "Colonist"), + FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.WOL)], "Colonist", number=7), + FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.WOL)], "Colonist", number=7, removal_priority=1), + FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.WOL)], "Artifact", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(7, SC2Campaign.WOL)], "Artifact", number=8, completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(8, SC2Campaign.WOL)], "Artifact", number=11, completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(9, SC2Campaign.WOL)], "Artifact", number=14, completion_critical=True, removal_priority=7), + FillMission(MissionPools.HARD, [MissionConnection(10, SC2Campaign.WOL)], "Artifact", completion_critical=True, removal_priority=6), + FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.WOL)], "Covert", number=4), + FillMission(MissionPools.MEDIUM, [MissionConnection(12, SC2Campaign.WOL)], "Covert"), + FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.WOL)], "Covert", number=8, removal_priority=3), + FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.WOL)], "Covert", number=8, removal_priority=2), + FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.WOL)], "Rebellion", number=6), + FillMission(MissionPools.HARD, [MissionConnection(16, SC2Campaign.WOL)], "Rebellion"), + FillMission(MissionPools.HARD, [MissionConnection(17, SC2Campaign.WOL)], "Rebellion"), + FillMission(MissionPools.HARD, [MissionConnection(18, SC2Campaign.WOL)], "Rebellion", removal_priority=8), + FillMission(MissionPools.HARD, [MissionConnection(19, SC2Campaign.WOL)], "Rebellion", removal_priority=5), + FillMission(MissionPools.HARD, [MissionConnection(11, SC2Campaign.WOL)], "Char", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(21, SC2Campaign.WOL)], "Char", completion_critical=True, removal_priority=4), + FillMission(MissionPools.HARD, [MissionConnection(21, SC2Campaign.WOL)], "Char", completion_critical=True), + FillMission(MissionPools.FINAL, [MissionConnection(22, SC2Campaign.WOL), MissionConnection(23, SC2Campaign.WOL)], "Char", completion_critical=True, or_requirements=True) + ], + SC2Campaign.PROPHECY: [ + FillMission(MissionPools.MEDIUM, [MissionConnection(8, SC2Campaign.WOL)], "_1"), + FillMission(MissionPools.HARD, [MissionConnection(0, SC2Campaign.PROPHECY)], "_2", removal_priority=2), + FillMission(MissionPools.HARD, [MissionConnection(1, SC2Campaign.PROPHECY)], "_3", removal_priority=1), + FillMission(MissionPools.FINAL, [MissionConnection(2, SC2Campaign.PROPHECY)], "_4"), + ], + SC2Campaign.HOTS: [ + FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.HOTS)], "Umoja", completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.HOTS)], "Umoja", completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.HOTS)], "Umoja", completion_critical=True, removal_priority=1), + FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.HOTS)], "Kaldir", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.HOTS)], "Kaldir", completion_critical=True, removal_priority=2), + FillMission(MissionPools.MEDIUM, [MissionConnection(4, SC2Campaign.HOTS)], "Kaldir", completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.HOTS)], "Char", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(6, SC2Campaign.HOTS)], "Char", completion_critical=True, removal_priority=3), + FillMission(MissionPools.MEDIUM, [MissionConnection(7, SC2Campaign.HOTS)], "Char", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS), MissionConnection(8, SC2Campaign.HOTS)], "Zerus", completion_critical=True, or_requirements=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(9, SC2Campaign.HOTS)], "Zerus", completion_critical=True, removal_priority=4), + FillMission(MissionPools.MEDIUM, [MissionConnection(10, SC2Campaign.HOTS)], "Zerus", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS), MissionConnection(8, SC2Campaign.HOTS), MissionConnection(11, SC2Campaign.HOTS)], "Skygeirr Station", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(12, SC2Campaign.HOTS)], "Skygeirr Station", completion_critical=True, removal_priority=5), + FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.HOTS)], "Skygeirr Station", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS), MissionConnection(8, SC2Campaign.HOTS), MissionConnection(11, SC2Campaign.HOTS)], "Dominion Space", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(15, SC2Campaign.HOTS)], "Dominion Space", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(14, SC2Campaign.HOTS), MissionConnection(16, SC2Campaign.HOTS)], "Korhal", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(17, SC2Campaign.HOTS)], "Korhal", completion_critical=True), + FillMission(MissionPools.FINAL, [MissionConnection(18, SC2Campaign.HOTS)], "Korhal", completion_critical=True), + ], + SC2Campaign.PROLOGUE: [ + FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.PROLOGUE)], "_1"), + FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.PROLOGUE)], "_2", removal_priority=1), + FillMission(MissionPools.FINAL, [MissionConnection(1, SC2Campaign.PROLOGUE)], "_3") + ], + SC2Campaign.LOTV: [ + FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.LOTV)], "Aiur", completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.LOTV)], "Aiur", completion_critical=True, removal_priority=3), + FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.LOTV)], "Aiur", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.LOTV)], "Korhal", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.LOTV)], "Korhal", completion_critical=True, removal_priority=7), + FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.LOTV)], "Shakuras", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.LOTV)], "Shakuras", completion_critical=True, removal_priority=6), + FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.LOTV), MissionConnection(6, SC2Campaign.LOTV)], "Purifier", completion_critical=True, or_requirements=True), + FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.LOTV), MissionConnection(6, SC2Campaign.LOTV), MissionConnection(7, SC2Campaign.LOTV)], "Ulnar", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(8, SC2Campaign.LOTV)], "Ulnar", completion_critical=True, removal_priority=1), + FillMission(MissionPools.HARD, [MissionConnection(9, SC2Campaign.LOTV)], "Ulnar", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(10, SC2Campaign.LOTV)], "Purifier", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(11, SC2Campaign.LOTV)], "Purifier", completion_critical=True, removal_priority=5), + FillMission(MissionPools.HARD, [MissionConnection(10, SC2Campaign.LOTV)], "Tal'darim", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.LOTV)], "Tal'darim", completion_critical=True, removal_priority=4), + FillMission(MissionPools.HARD, [MissionConnection(12, SC2Campaign.LOTV), MissionConnection(14, SC2Campaign.LOTV)], "Moebius", completion_critical=True, or_requirements=True), + FillMission(MissionPools.HARD, [MissionConnection(12, SC2Campaign.LOTV), MissionConnection(14, SC2Campaign.LOTV), MissionConnection(15, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(16, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True, removal_priority=2), + FillMission(MissionPools.FINAL, [MissionConnection(17, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True), + ], + SC2Campaign.EPILOGUE: [ + FillMission(MissionPools.VERY_HARD, [MissionConnection(24, SC2Campaign.WOL), MissionConnection(19, SC2Campaign.HOTS), MissionConnection(18, SC2Campaign.LOTV)], "_1", completion_critical=True), + FillMission(MissionPools.VERY_HARD, [MissionConnection(0, SC2Campaign.EPILOGUE)], "_2", completion_critical=True, removal_priority=1), + FillMission(MissionPools.FINAL, [MissionConnection(1, SC2Campaign.EPILOGUE)], "_3", completion_critical=True), + ], + SC2Campaign.NCO: [ + FillMission(MissionPools.EASY, [MissionConnection(-1, SC2Campaign.NCO)], "_1", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.NCO)], "_1", completion_critical=True, removal_priority=6), + FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.NCO)], "_1", completion_critical=True, removal_priority=5), + FillMission(MissionPools.HARD, [MissionConnection(2, SC2Campaign.NCO)], "_2", completion_critical=True, removal_priority=7), + FillMission(MissionPools.HARD, [MissionConnection(3, SC2Campaign.NCO)], "_2", completion_critical=True, removal_priority=4), + FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.NCO)], "_2", completion_critical=True, removal_priority=3), + FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.NCO)], "_3", completion_critical=True, removal_priority=2), + FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.NCO)], "_3", completion_critical=True, removal_priority=1), + FillMission(MissionPools.FINAL, [MissionConnection(7, SC2Campaign.NCO)], "_3", completion_critical=True), + ] + } + + +def mini_campaign_order() -> Dict[SC2Campaign, List[FillMission]]: + return { + SC2Campaign.WOL: [ + FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.WOL)], "Mar Sara", completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.WOL)], "Colonist"), + FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.WOL)], "Colonist"), + FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.WOL)], "Artifact", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.WOL)], "Artifact", number=4, completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.WOL)], "Artifact", number=8, completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.WOL)], "Covert", number=2), + FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.WOL)], "Covert"), + FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.WOL)], "Rebellion", number=3), + FillMission(MissionPools.HARD, [MissionConnection(8, SC2Campaign.WOL)], "Rebellion"), + FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.WOL)], "Char", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.WOL)], "Char", completion_critical=True), + FillMission(MissionPools.FINAL, [MissionConnection(10, SC2Campaign.WOL), MissionConnection(11, SC2Campaign.WOL)], "Char", completion_critical=True, or_requirements=True) + ], + SC2Campaign.PROPHECY: [ + FillMission(MissionPools.MEDIUM, [MissionConnection(4, SC2Campaign.WOL)], "_1"), + FillMission(MissionPools.FINAL, [MissionConnection(0, SC2Campaign.PROPHECY)], "_2"), + ], + SC2Campaign.HOTS: [ + FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.HOTS)], "Umoja", completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.HOTS)], "Kaldir"), + FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.HOTS)], "Kaldir"), + FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.HOTS)], "Char"), + FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.HOTS)], "Char"), + FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.HOTS)], "Zerus", number=3), + FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS)], "Zerus"), + FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.HOTS)], "Skygeirr Station", number=5), + FillMission(MissionPools.HARD, [MissionConnection(7, SC2Campaign.HOTS)], "Skygeirr Station"), + FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.HOTS)], "Dominion Space", number=5), + FillMission(MissionPools.HARD, [MissionConnection(9, SC2Campaign.HOTS)], "Dominion Space"), + FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.HOTS)], "Korhal", completion_critical=True, number=8), + FillMission(MissionPools.FINAL, [MissionConnection(11, SC2Campaign.HOTS)], "Korhal", completion_critical=True), + ], + SC2Campaign.PROLOGUE: [ + FillMission(MissionPools.EASY, [MissionConnection(-1, SC2Campaign.PROLOGUE)], "_1"), + FillMission(MissionPools.FINAL, [MissionConnection(0, SC2Campaign.PROLOGUE)], "_2") + ], + SC2Campaign.LOTV: [ + FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.LOTV)], "Aiur",completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.LOTV)], "Aiur", completion_critical=True), + FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.LOTV)], "Korhal", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.LOTV)], "Shakuras", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.LOTV), MissionConnection(3, SC2Campaign.LOTV)], "Purifier", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.LOTV)], "Purifier", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.LOTV)], "Ulnar", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.LOTV)], "Tal'darim", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.LOTV), MissionConnection(7, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True), + FillMission(MissionPools.FINAL, [MissionConnection(8, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True), + ], + SC2Campaign.EPILOGUE: [ + FillMission(MissionPools.VERY_HARD, [MissionConnection(12, SC2Campaign.WOL), MissionConnection(12, SC2Campaign.HOTS), MissionConnection(9, SC2Campaign.LOTV)], "_1", completion_critical=True), + FillMission(MissionPools.FINAL, [MissionConnection(0, SC2Campaign.EPILOGUE)], "_2", completion_critical=True), + ], + SC2Campaign.NCO: [ + FillMission(MissionPools.EASY, [MissionConnection(-1, SC2Campaign.NCO)], "_1", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.NCO)], "_1", completion_critical=True), + FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.NCO)], "_2", completion_critical=True), + FillMission(MissionPools.HARD, [MissionConnection(2, SC2Campaign.NCO)], "_3", completion_critical=True), + FillMission(MissionPools.FINAL, [MissionConnection(3, SC2Campaign.NCO)], "_3", completion_critical=True), + ] + } + + +def make_golden_path(world: World, num_missions: int) -> list[FillMission]: + chain_name_options = ['Mar Sara', 'Char', 'Kaldir', 'Zerus', 'Skygeirr Station', + 'Dominion Space', 'Korhal', 'Aiur', 'Shakuras', 'Ulnar'] + world.random.shuffle(chain_name_options) + + mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] + first_chain = chain_name_options.pop() + first_mission = FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.GLOBAL)], first_chain, + completion_critical=True) + + class Campaign: + def __init__(self, first_mission: FillMission, missions_remaining: int): + self.mission_order = [first_mission] + self.counter = 0 + self.last_mission_in_chain = [0] + self.chain_names = [first_mission.category] + self.missions_remaining = missions_remaining + self.padding = 0 + + def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): + if self.missions_remaining == 0 and difficulty is not MissionPools.FINAL: + return + self.counter += 1 + if self.mission_order[self.last_mission_in_chain[chain]].number == required_missions or required_missions <= 1: + required_missions = 0 + mission_connections = [MissionConnection(self.last_mission_in_chain[chain], SC2Campaign.GLOBAL)] + padding = 0 + if self.last_mission_in_chain[chain] == self.last_mission_in_chain[0]: + # Adding padding to the start of new chains + if chain != 0: + padding = self.padding + else: + # Requiring main chain progress for optional chains + mission_connections.append(MissionConnection(self.last_mission_in_chain[0], SC2Campaign.GLOBAL)) + self.mission_order.append(FillMission( + difficulty, + mission_connections, + self.chain_names[chain], + number=required_missions, + completion_critical=chain == 0, + ui_vertical_padding=padding + )) + self.last_mission_in_chain[chain] = self.counter + if chain == 0: + self.padding += 1 + self.missions_remaining -= 1 + + campaign = Campaign(first_mission, num_missions - 2) + current_required_missions = 0 + main_chain = 0 + while campaign.missions_remaining > 0: + main_chain += 1 + mission_difficulty = mission_difficulties[min(main_chain // 2, 3)] + if main_chain % 2 == 1: # Adding branches + chains_to_make = 0 if len(campaign.chain_names) > 5 else 2 if main_chain == 1 else 1 + for new_chain in range(chains_to_make): + campaign.chain_names.append(chain_name_options.pop()) + campaign.last_mission_in_chain.append(campaign.last_mission_in_chain[0]) + # Updating branches + for side_chain in range(len(campaign.chain_names) - 1, 0, -1): + campaign.add_mission(side_chain, mission_difficulty) + # Adding main path mission + current_required_missions = (len(campaign.mission_order) * 3) // 4 + campaign.add_mission(0, mission_difficulty, current_required_missions) + campaign.add_mission(0, MissionPools.FINAL, current_required_missions) + return {SC2Campaign.GLOBAL: campaign.mission_order} + + +def make_gauntlet(num_missions: int) -> list[FillMission]: + mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] + mission_order: list[FillMission] = [] + row_length = 7 + rows = math.ceil(num_missions / row_length) + if rows == 1: + column_names = ["I", "II", "III", "IV", "V", "VI", "VII"][:num_missions - 1] + column_names.append("Final") + else: + column_names = [f'_{col + 1}' for col in range(row_length)] + first_mission = FillMission(MissionPools.STARTER, [MissionConnection(-1)], column_names[0], + completion_critical=True) + mission_order.append(first_mission) + mission_number = 1 + space_rows = 0 + while mission_number < num_missions: + if mission_number == num_missions - 1: + difficulty = MissionPools.FINAL + else: + difficulty_progress = math.floor(min(mission_number * 4 / num_missions, mission_number / 3)) + difficulty = mission_difficulties[min(difficulty_progress, 3)] + connection = MissionConnection(mission_number - 1) + mission_order.append(FillMission( + difficulty, + [connection], + column_names[mission_number % row_length], + completion_critical=True, + ui_vertical_padding=space_rows + )) + mission_number += 1 + # Next row + if mission_number == row_length: + space_rows += 1 + return {SC2Campaign.GLOBAL: mission_order} + + +def make_blitz(num_missions: int) -> list[FillMission]: + mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] + min_width, max_width = 2, 5 + mission_divisor = 5 + dynamic_width = num_missions / mission_divisor + width = max(min(dynamic_width, max_width), min_width) + middle_column = math.floor(width / 2) + connections = [MissionConnection(-1)] + mission_number = 0 + mission_order: List[FillMission] = [] + if num_missions % width > middle_column: + final_row = math.floor(num_missions / width) * width + final_mission_number = final_row + middle_column + else: + final_mission_number = num_missions - 1 + while mission_number < num_missions: + column = mission_number % width + if mission_number == middle_column: + difficulty = MissionPools.STARTER + elif mission_number == final_mission_number: + difficulty = MissionPools.FINAL + else: + difficulty = mission_difficulties[math.floor(min(mission_number / width, 3))] + mission_order.append(FillMission( + difficulty, + connections, + f'_{column}', + or_requirements=True + )) + mission_number += 1 + # Next row, requires previous row + if mission_number % width == 0: + connections = [MissionConnection(mission_number - 1 - i) for i in range(width)] + connections.reverse() + return {SC2Campaign.GLOBAL: mission_order} diff --git a/worlds/sc2/MissionTables.py b/worlds/sc2/MissionTables.py index cf7acace8d4a..2df918fc8e94 100644 --- a/worlds/sc2/MissionTables.py +++ b/worlds/sc2/MissionTables.py @@ -231,174 +231,6 @@ class FillMission(NamedTuple): ui_vertical_padding: int = 0 # How many blank padding tiles go above this mission in the launcher -def vanilla_shuffle_order() -> Dict[SC2Campaign, List[FillMission]]: - return { - SC2Campaign.WOL: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.WOL)], "Mar Sara", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.WOL)], "Mar Sara", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.WOL)], "Mar Sara", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.WOL)], "Colonist"), - FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.WOL)], "Colonist"), - FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.WOL)], "Colonist", number=7), - FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.WOL)], "Colonist", number=7, removal_priority=1), - FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.WOL)], "Artifact", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(7, SC2Campaign.WOL)], "Artifact", number=8, completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(8, SC2Campaign.WOL)], "Artifact", number=11, completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(9, SC2Campaign.WOL)], "Artifact", number=14, completion_critical=True, removal_priority=7), - FillMission(MissionPools.HARD, [MissionConnection(10, SC2Campaign.WOL)], "Artifact", completion_critical=True, removal_priority=6), - FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.WOL)], "Covert", number=4), - FillMission(MissionPools.MEDIUM, [MissionConnection(12, SC2Campaign.WOL)], "Covert"), - FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.WOL)], "Covert", number=8, removal_priority=3), - FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.WOL)], "Covert", number=8, removal_priority=2), - FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.WOL)], "Rebellion", number=6), - FillMission(MissionPools.HARD, [MissionConnection(16, SC2Campaign.WOL)], "Rebellion"), - FillMission(MissionPools.HARD, [MissionConnection(17, SC2Campaign.WOL)], "Rebellion"), - FillMission(MissionPools.HARD, [MissionConnection(18, SC2Campaign.WOL)], "Rebellion", removal_priority=8), - FillMission(MissionPools.HARD, [MissionConnection(19, SC2Campaign.WOL)], "Rebellion", removal_priority=5), - FillMission(MissionPools.HARD, [MissionConnection(11, SC2Campaign.WOL)], "Char", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(21, SC2Campaign.WOL)], "Char", completion_critical=True, removal_priority=4), - FillMission(MissionPools.HARD, [MissionConnection(21, SC2Campaign.WOL)], "Char", completion_critical=True), - FillMission(MissionPools.FINAL, [MissionConnection(22, SC2Campaign.WOL), MissionConnection(23, SC2Campaign.WOL)], "Char", completion_critical=True, or_requirements=True) - ], - SC2Campaign.PROPHECY: [ - FillMission(MissionPools.MEDIUM, [MissionConnection(8, SC2Campaign.WOL)], "_1"), - FillMission(MissionPools.HARD, [MissionConnection(0, SC2Campaign.PROPHECY)], "_2", removal_priority=2), - FillMission(MissionPools.HARD, [MissionConnection(1, SC2Campaign.PROPHECY)], "_3", removal_priority=1), - FillMission(MissionPools.FINAL, [MissionConnection(2, SC2Campaign.PROPHECY)], "_4"), - ], - SC2Campaign.HOTS: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.HOTS)], "Umoja", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.HOTS)], "Umoja", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.HOTS)], "Umoja", completion_critical=True, removal_priority=1), - FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.HOTS)], "Kaldir", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.HOTS)], "Kaldir", completion_critical=True, removal_priority=2), - FillMission(MissionPools.MEDIUM, [MissionConnection(4, SC2Campaign.HOTS)], "Kaldir", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.HOTS)], "Char", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(6, SC2Campaign.HOTS)], "Char", completion_critical=True, removal_priority=3), - FillMission(MissionPools.MEDIUM, [MissionConnection(7, SC2Campaign.HOTS)], "Char", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS), MissionConnection(8, SC2Campaign.HOTS)], "Zerus", completion_critical=True, or_requirements=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(9, SC2Campaign.HOTS)], "Zerus", completion_critical=True, removal_priority=4), - FillMission(MissionPools.MEDIUM, [MissionConnection(10, SC2Campaign.HOTS)], "Zerus", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS), MissionConnection(8, SC2Campaign.HOTS), MissionConnection(11, SC2Campaign.HOTS)], "Skygeirr Station", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(12, SC2Campaign.HOTS)], "Skygeirr Station", completion_critical=True, removal_priority=5), - FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.HOTS)], "Skygeirr Station", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS), MissionConnection(8, SC2Campaign.HOTS), MissionConnection(11, SC2Campaign.HOTS)], "Dominion Space", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(15, SC2Campaign.HOTS)], "Dominion Space", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(14, SC2Campaign.HOTS), MissionConnection(16, SC2Campaign.HOTS)], "Korhal", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(17, SC2Campaign.HOTS)], "Korhal", completion_critical=True), - FillMission(MissionPools.FINAL, [MissionConnection(18, SC2Campaign.HOTS)], "Korhal", completion_critical=True), - ], - SC2Campaign.PROLOGUE: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.PROLOGUE)], "_1"), - FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.PROLOGUE)], "_2", removal_priority=1), - FillMission(MissionPools.FINAL, [MissionConnection(1, SC2Campaign.PROLOGUE)], "_3") - ], - SC2Campaign.LOTV: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.LOTV)], "Aiur", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.LOTV)], "Aiur", completion_critical=True, removal_priority=3), - FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.LOTV)], "Aiur", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.LOTV)], "Korhal", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.LOTV)], "Korhal", completion_critical=True, removal_priority=7), - FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.LOTV)], "Shakuras", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.LOTV)], "Shakuras", completion_critical=True, removal_priority=6), - FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.LOTV), MissionConnection(6, SC2Campaign.LOTV)], "Purifier", completion_critical=True, or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.LOTV), MissionConnection(6, SC2Campaign.LOTV), MissionConnection(7, SC2Campaign.LOTV)], "Ulnar", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(8, SC2Campaign.LOTV)], "Ulnar", completion_critical=True, removal_priority=1), - FillMission(MissionPools.HARD, [MissionConnection(9, SC2Campaign.LOTV)], "Ulnar", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(10, SC2Campaign.LOTV)], "Purifier", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(11, SC2Campaign.LOTV)], "Purifier", completion_critical=True, removal_priority=5), - FillMission(MissionPools.HARD, [MissionConnection(10, SC2Campaign.LOTV)], "Tal'darim", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.LOTV)], "Tal'darim", completion_critical=True, removal_priority=4), - FillMission(MissionPools.HARD, [MissionConnection(12, SC2Campaign.LOTV), MissionConnection(14, SC2Campaign.LOTV)], "Moebius", completion_critical=True, or_requirements=True), - FillMission(MissionPools.HARD, [MissionConnection(12, SC2Campaign.LOTV), MissionConnection(14, SC2Campaign.LOTV), MissionConnection(15, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(16, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True, removal_priority=2), - FillMission(MissionPools.FINAL, [MissionConnection(17, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True), - ], - SC2Campaign.EPILOGUE: [ - FillMission(MissionPools.VERY_HARD, [MissionConnection(24, SC2Campaign.WOL), MissionConnection(19, SC2Campaign.HOTS), MissionConnection(18, SC2Campaign.LOTV)], "_1", completion_critical=True), - FillMission(MissionPools.VERY_HARD, [MissionConnection(0, SC2Campaign.EPILOGUE)], "_2", completion_critical=True, removal_priority=1), - FillMission(MissionPools.FINAL, [MissionConnection(1, SC2Campaign.EPILOGUE)], "_3", completion_critical=True), - ], - SC2Campaign.NCO: [ - FillMission(MissionPools.EASY, [MissionConnection(-1, SC2Campaign.NCO)], "_1", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.NCO)], "_1", completion_critical=True, removal_priority=6), - FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.NCO)], "_1", completion_critical=True, removal_priority=5), - FillMission(MissionPools.HARD, [MissionConnection(2, SC2Campaign.NCO)], "_2", completion_critical=True, removal_priority=7), - FillMission(MissionPools.HARD, [MissionConnection(3, SC2Campaign.NCO)], "_2", completion_critical=True, removal_priority=4), - FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.NCO)], "_2", completion_critical=True, removal_priority=3), - FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.NCO)], "_3", completion_critical=True, removal_priority=2), - FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.NCO)], "_3", completion_critical=True, removal_priority=1), - FillMission(MissionPools.FINAL, [MissionConnection(7, SC2Campaign.NCO)], "_3", completion_critical=True), - ] - } - - -def mini_campaign_order() -> Dict[SC2Campaign, List[FillMission]]: - return { - SC2Campaign.WOL: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.WOL)], "Mar Sara", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.WOL)], "Colonist"), - FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.WOL)], "Colonist"), - FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.WOL)], "Artifact", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.WOL)], "Artifact", number=4, completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.WOL)], "Artifact", number=8, completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.WOL)], "Covert", number=2), - FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.WOL)], "Covert"), - FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.WOL)], "Rebellion", number=3), - FillMission(MissionPools.HARD, [MissionConnection(8, SC2Campaign.WOL)], "Rebellion"), - FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.WOL)], "Char", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.WOL)], "Char", completion_critical=True), - FillMission(MissionPools.FINAL, [MissionConnection(10, SC2Campaign.WOL), MissionConnection(11, SC2Campaign.WOL)], "Char", completion_critical=True, or_requirements=True) - ], - SC2Campaign.PROPHECY: [ - FillMission(MissionPools.MEDIUM, [MissionConnection(4, SC2Campaign.WOL)], "_1"), - FillMission(MissionPools.FINAL, [MissionConnection(0, SC2Campaign.PROPHECY)], "_2"), - ], - SC2Campaign.HOTS: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.HOTS)], "Umoja", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.HOTS)], "Kaldir"), - FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.HOTS)], "Kaldir"), - FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.HOTS)], "Char"), - FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.HOTS)], "Char"), - FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.HOTS)], "Zerus", number=3), - FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS)], "Zerus"), - FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.HOTS)], "Skygeirr Station", number=5), - FillMission(MissionPools.HARD, [MissionConnection(7, SC2Campaign.HOTS)], "Skygeirr Station"), - FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.HOTS)], "Dominion Space", number=5), - FillMission(MissionPools.HARD, [MissionConnection(9, SC2Campaign.HOTS)], "Dominion Space"), - FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.HOTS)], "Korhal", completion_critical=True, number=8), - FillMission(MissionPools.FINAL, [MissionConnection(11, SC2Campaign.HOTS)], "Korhal", completion_critical=True), - ], - SC2Campaign.PROLOGUE: [ - FillMission(MissionPools.EASY, [MissionConnection(-1, SC2Campaign.PROLOGUE)], "_1"), - FillMission(MissionPools.FINAL, [MissionConnection(0, SC2Campaign.PROLOGUE)], "_2") - ], - SC2Campaign.LOTV: [ - FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.LOTV)], "Aiur",completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.LOTV)], "Aiur", completion_critical=True), - FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.LOTV)], "Korhal", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.LOTV)], "Shakuras", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.LOTV), MissionConnection(3, SC2Campaign.LOTV)], "Purifier", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.LOTV)], "Purifier", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.LOTV)], "Ulnar", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.LOTV)], "Tal'darim", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.LOTV), MissionConnection(7, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True), - FillMission(MissionPools.FINAL, [MissionConnection(8, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True), - ], - SC2Campaign.EPILOGUE: [ - FillMission(MissionPools.VERY_HARD, [MissionConnection(12, SC2Campaign.WOL), MissionConnection(12, SC2Campaign.HOTS), MissionConnection(9, SC2Campaign.LOTV)], "_1", completion_critical=True), - FillMission(MissionPools.FINAL, [MissionConnection(0, SC2Campaign.EPILOGUE)], "_2", completion_critical=True), - ], - SC2Campaign.NCO: [ - FillMission(MissionPools.EASY, [MissionConnection(-1, SC2Campaign.NCO)], "_1", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.NCO)], "_1", completion_critical=True), - FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.NCO)], "_2", completion_critical=True), - FillMission(MissionPools.HARD, [MissionConnection(2, SC2Campaign.NCO)], "_3", completion_critical=True), - FillMission(MissionPools.FINAL, [MissionConnection(3, SC2Campaign.NCO)], "_3", completion_critical=True), - ] - } - - vanilla_mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]] = { SC2Campaign.WOL: { SC2Mission.LIBERATION_DAY.mission_name: MissionInfo(SC2Mission.LIBERATION_DAY, [], SC2Mission.LIBERATION_DAY.area, completion_critical=True), diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index e1971f2aea97..8b89f3a9c0ec 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -3,7 +3,8 @@ from Options import Choice, Toggle, DefaultOnToggle, ItemSet, OptionSet, Range, PerGameCommonOptions from .MissionTables import SC2Campaign, SC2Mission, lookup_name_to_mission, MissionPools, get_no_build_missions, \ - campaign_mission_table, vanilla_shuffle_order, mini_campaign_order + campaign_mission_table +from .MissionOrders import vanilla_shuffle_order, mini_campaign_order from worlds.AutoWorld import World diff --git a/worlds/sc2/Regions.py b/worlds/sc2/Regions.py index 37f1115dcfd0..a6597e195b76 100644 --- a/worlds/sc2/Regions.py +++ b/worlds/sc2/Regions.py @@ -6,7 +6,8 @@ from .Options import get_option_value, MissionOrder, get_enabled_campaigns, campaign_depending_orders, \ GridTwoStartPositions, static_mission_orders, dynamic_mission_orders from .MissionTables import MissionInfo, vanilla_mission_req_table, \ - MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection, FillMission + MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection +from .MissionOrders import make_gauntlet, make_blitz, make_golden_path from .PoolFilter import filter_missions from worlds.AutoWorld import World @@ -395,147 +396,6 @@ def make_dynamic_mission_order( return make_blitz(num_missions) -def make_golden_path(world, num_missions) -> list[FillMission]: - chain_name_options = ['Mar Sara', 'Char', 'Kaldir', 'Zerus', 'Skygeirr Station', - 'Dominion Space', 'Korhal', 'Aiur', 'Shakuras', 'Ulnar'] - world.random.shuffle(chain_name_options) - - mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] - first_chain = chain_name_options.pop() - first_mission = FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.GLOBAL)], first_chain, - completion_critical=True) - - class Campaign: - def __init__(self, first_mission: FillMission, missions_remaining: int): - self.mission_order = [first_mission] - self.counter = 0 - self.last_mission_in_chain = [0] - self.chain_names = [first_mission.category] - self.missions_remaining = missions_remaining - self.padding = 0 - - def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): - if self.missions_remaining == 0 and difficulty is not MissionPools.FINAL: - return - self.counter += 1 - if self.mission_order[self.last_mission_in_chain[chain]].number == required_missions or required_missions <= 1: - required_missions = 0 - mission_connections = [MissionConnection(self.last_mission_in_chain[chain], SC2Campaign.GLOBAL)] - padding = 0 - if self.last_mission_in_chain[chain] == self.last_mission_in_chain[0]: - # Adding padding to the start of new chains - if chain != 0: - padding = self.padding - else: - # Requiring main chain progress for optional chains - mission_connections.append(MissionConnection(self.last_mission_in_chain[0], SC2Campaign.GLOBAL)) - self.mission_order.append(FillMission( - difficulty, - mission_connections, - self.chain_names[chain], - number=required_missions, - completion_critical=chain == 0, - ui_vertical_padding=padding - )) - self.last_mission_in_chain[chain] = self.counter - if chain == 0: - self.padding += 1 - self.missions_remaining -= 1 - - campaign = Campaign(first_mission, num_missions - 2) - current_required_missions = 0 - main_chain = 0 - while campaign.missions_remaining > 0: - main_chain += 1 - mission_difficulty = mission_difficulties[min(main_chain // 2, 3)] - if main_chain % 2 == 1: # Adding branches - chains_to_make = 0 if len(campaign.chain_names) > 5 else 2 if main_chain == 1 else 1 - for new_chain in range(chains_to_make): - campaign.chain_names.append(chain_name_options.pop()) - campaign.last_mission_in_chain.append(campaign.last_mission_in_chain[0]) - # Updating branches - for side_chain in range(len(campaign.chain_names) - 1, 0, -1): - campaign.add_mission(side_chain, mission_difficulty) - # Adding main path mission - current_required_missions = (len(campaign.mission_order) * 3) // 4 - campaign.add_mission(0, mission_difficulty, current_required_missions) - campaign.add_mission(0, MissionPools.FINAL, current_required_missions) - return {SC2Campaign.GLOBAL: campaign.mission_order} - - -def make_gauntlet(num_missions) -> list[FillMission]: - mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] - mission_order: list[FillMission] = [] - row_length = 7 - rows = math.ceil(num_missions / row_length) - if rows == 1: - column_names = ["I", "II", "III", "IV", "V", "VI", "VII"][:num_missions - 1] - column_names.append("Final") - else: - column_names = [f'_{col + 1}' for col in range(row_length)] - first_mission = FillMission(MissionPools.STARTER, [MissionConnection(-1)], column_names[0], - completion_critical=True) - mission_order.append(first_mission) - mission_number = 1 - space_rows = 0 - while mission_number < num_missions: - if mission_number == num_missions - 1: - difficulty = MissionPools.FINAL - else: - difficulty_progress = math.floor(min(mission_number * 4 / num_missions, mission_number / 3)) - difficulty = mission_difficulties[min(difficulty_progress, 3)] - connection = MissionConnection(mission_number - 1) - mission_order.append(FillMission( - difficulty, - [connection], - column_names[mission_number % row_length], - completion_critical=True, - ui_vertical_padding=space_rows - )) - mission_number += 1 - # Next row - if mission_number == row_length: - space_rows += 1 - return {SC2Campaign.GLOBAL: mission_order} - - -def make_blitz(num_missions) -> list[FillMission]: - mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] - min_width, max_width = 2, 5 - mission_divisor = 5 - dynamic_width = num_missions / mission_divisor - width = max(min(dynamic_width, max_width), min_width) - middle_column = math.floor(width / 2) - connections = [MissionConnection(-1)] - mission_number = 0 - mission_order: List[FillMission] = [] - if num_missions % width > middle_column: - final_row = math.floor(num_missions / width) * width - final_mission_number = final_row + middle_column - else: - final_mission_number = num_missions - 1 - while mission_number < num_missions: - column = mission_number % width - if mission_number == middle_column: - difficulty = MissionPools.STARTER - elif mission_number == final_mission_number: - difficulty = MissionPools.FINAL - else: - difficulty = mission_difficulties[math.floor(min(mission_number / width, 3))] - mission_order.append(FillMission( - difficulty, - connections, - f'_{column}', - or_requirements=True - )) - mission_number += 1 - # Next row, requires previous row - if mission_number % width == 0: - connections = [MissionConnection(mission_number - 1 - i) for i in range(width)] - connections.reverse() - return {SC2Campaign.GLOBAL: mission_order} - - def create_structured_regions( world: World, locations: Tuple[LocationData, ...], From 4a0c606054079917247834255f5815ce300281a7 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sun, 5 May 2024 13:22:33 -0400 Subject: [PATCH 06/24] Diagonal mission order, difficulty scaling adjustments Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 89 ++++++++++++++++++++++++++++++++++--- worlds/sc2/Options.py | 4 +- worlds/sc2/Regions.py | 4 +- 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py index c9a204d8154f..e44eb57b1645 100644 --- a/worlds/sc2/MissionOrders.py +++ b/worlds/sc2/MissionOrders.py @@ -173,12 +173,18 @@ def mini_campaign_order() -> Dict[SC2Campaign, List[FillMission]]: } +smooth_difficulty = [MissionPools.EASY, + MissionPools.MEDIUM, MissionPools.MEDIUM, + MissionPools.HARD, MissionPools.HARD, + MissionPools.VERY_HARD] +max_difficulty = len(smooth_difficulty) - 1 + + def make_golden_path(world: World, num_missions: int) -> list[FillMission]: chain_name_options = ['Mar Sara', 'Char', 'Kaldir', 'Zerus', 'Skygeirr Station', 'Dominion Space', 'Korhal', 'Aiur', 'Shakuras', 'Ulnar'] world.random.shuffle(chain_name_options) - mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] first_chain = chain_name_options.pop() first_mission = FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.GLOBAL)], first_chain, completion_critical=True) @@ -224,8 +230,8 @@ def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): current_required_missions = 0 main_chain = 0 while campaign.missions_remaining > 0: + mission_difficulty = smooth_difficulty[min(main_chain, max_difficulty)] main_chain += 1 - mission_difficulty = mission_difficulties[min(main_chain // 2, 3)] if main_chain % 2 == 1: # Adding branches chains_to_make = 0 if len(campaign.chain_names) > 5 else 2 if main_chain == 1 else 1 for new_chain in range(chains_to_make): @@ -242,10 +248,10 @@ def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): def make_gauntlet(num_missions: int) -> list[FillMission]: - mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] mission_order: list[FillMission] = [] row_length = 7 rows = math.ceil(num_missions / row_length) + difficulty_rate = (max_difficulty + 1) / num_missions if num_missions < 21 else 1/3 if rows == 1: column_names = ["I", "II", "III", "IV", "V", "VI", "VII"][:num_missions - 1] column_names.append("Final") @@ -260,8 +266,8 @@ def make_gauntlet(num_missions: int) -> list[FillMission]: if mission_number == num_missions - 1: difficulty = MissionPools.FINAL else: - difficulty_progress = math.floor(min(mission_number * 4 / num_missions, mission_number / 3)) - difficulty = mission_difficulties[min(difficulty_progress, 3)] + difficulty_progress = mission_number * difficulty_rate + difficulty = smooth_difficulty[min(math.floor(difficulty_progress), max_difficulty)] connection = MissionConnection(mission_number - 1) mission_order.append(FillMission( difficulty, @@ -278,7 +284,6 @@ def make_gauntlet(num_missions: int) -> list[FillMission]: def make_blitz(num_missions: int) -> list[FillMission]: - mission_difficulties = [MissionPools.EASY, MissionPools.MEDIUM, MissionPools.HARD, MissionPools.VERY_HARD] min_width, max_width = 2, 5 mission_divisor = 5 dynamic_width = num_missions / mission_divisor @@ -294,12 +299,13 @@ def make_blitz(num_missions: int) -> list[FillMission]: final_mission_number = num_missions - 1 while mission_number < num_missions: column = mission_number % width + row = math.floor(mission_number / width) if mission_number == middle_column: difficulty = MissionPools.STARTER elif mission_number == final_mission_number: difficulty = MissionPools.FINAL else: - difficulty = mission_difficulties[math.floor(min(mission_number / width, 3))] + difficulty = smooth_difficulty[(min(row, max_difficulty))] mission_order.append(FillMission( difficulty, connections, @@ -312,3 +318,72 @@ def make_blitz(num_missions: int) -> list[FillMission]: connections = [MissionConnection(mission_number - 1 - i) for i in range(width)] connections.reverse() return {SC2Campaign.GLOBAL: mission_order} + + +def make_diagonal(two_start_positions: bool, num_missions: int): + mission_order: List[FillMission] = [] + menu_connection = [MissionConnection(-1)] + max_width = 7 + difficulty_progress = 0 + difficulty_rate = max(0.5, min(1, 3 * max_difficulty / num_missions)) + x = 0 + y = 0 + difficulty = MissionPools.EASY + first_diagonal = True + creation_cycle = 0 # Root -> Bottom Branch -> Right Branch + # Creating the starter missions + if two_start_positions: + mission_order += [ + FillMission(MissionPools.STARTER, menu_connection, '_1', ui_vertical_padding=1), + FillMission(MissionPools.EASY, menu_connection, '_2') + ] + mission_number = 2 + else: + mission_order.append(FillMission(MissionPools.STARTER, menu_connection, '_1', completion_critical=True)) + creation_cycle = 1 + mission_number = 1 + while mission_number < num_missions: + if mission_number == num_missions - 1: + difficulty = MissionPools.FINAL + elif creation_cycle == 0 and difficulty_progress < max_difficulty: + difficulty_progress += difficulty_rate + difficulty = smooth_difficulty[math.floor(difficulty_progress)] + if creation_cycle == 0: + # Root + if y != -1: + x += 1 + y += 1 + mission_order.append(FillMission( + difficulty, + [MissionConnection(mission_number - 1), MissionConnection(mission_number - 2)], + f'_{x + 1}', + or_requirements=True, + completion_critical=True + )) + elif creation_cycle == 1: + # Bottom branch + mission_order.append(FillMission( + difficulty, + [MissionConnection(mission_number - 1)], + f'_{x + 1}' + )) + else: + # Right branch + column = f'_{x + 2}' + if x >= max_width - 1: + # Wrapping around + x = 0 + y = -1 + column = '_1' + first_diagonal = False + mission_order.append(FillMission( + difficulty, + [MissionConnection(mission_number - 2)], + column, + ui_vertical_padding=y if first_diagonal else 1 + )) + mission_number += 1 + creation_cycle += 1 + if creation_cycle == 3: + creation_cycle = 0 + return {SC2Campaign.GLOBAL: mission_order} diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index 8b89f3a9c0ec..4c17e05702d8 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -75,6 +75,7 @@ class MissionOrder(Choice): option_gauntlet = 6 option_grid = 9 option_golden_path = 10 + option_diagonal = 11 class MaximumCampaignSize(Range): @@ -909,7 +910,8 @@ def get_excluded_missions(world: World) -> Set[SC2Mission]: MissionOrder.option_golden_path, MissionOrder.option_grid, MissionOrder.option_gauntlet, - MissionOrder.option_blitz + MissionOrder.option_blitz, + MissionOrder.option_diagonal ] kerrigan_unit_available = [ diff --git a/worlds/sc2/Regions.py b/worlds/sc2/Regions.py index a6597e195b76..cc7d26db7d9a 100644 --- a/worlds/sc2/Regions.py +++ b/worlds/sc2/Regions.py @@ -7,7 +7,7 @@ GridTwoStartPositions, static_mission_orders, dynamic_mission_orders from .MissionTables import MissionInfo, vanilla_mission_req_table, \ MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection -from .MissionOrders import make_gauntlet, make_blitz, make_golden_path +from .MissionOrders import make_gauntlet, make_blitz, make_golden_path, make_diagonal from .PoolFilter import filter_missions from worlds.AutoWorld import World @@ -394,6 +394,8 @@ def make_dynamic_mission_order( return make_gauntlet(num_missions) elif mission_order_type == MissionOrder.option_blitz: return make_blitz(num_missions) + elif mission_order_type == MissionOrder.option_diagonal: + return make_diagonal(world.options.grid_two_start_positions, num_missions) def create_structured_regions( From c162faf47c7840b46e78cde1df61f7786bbcec19 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sun, 5 May 2024 13:41:29 -0400 Subject: [PATCH 07/24] Handling mission count checks on dynamic mission orders Signed-off-by: Magnemania --- worlds/sc2/Options.py | 13 +++---------- worlds/sc2/PoolFilter.py | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index 4c17e05702d8..69c4dffd88c6 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -869,16 +869,9 @@ def get_excluded_missions(world: World) -> Set[SC2Mission]: excluded_missions: Set[SC2Mission] = set([lookup_name_to_mission[name] for name in excluded_mission_names]) # Excluding Very Hard missions depending on options - if (get_option_value(world, "exclude_very_hard_missions") == ExcludeVeryHardMissions.option_true - ) or ( - get_option_value(world, "exclude_very_hard_missions") == ExcludeVeryHardMissions.option_default - and ( - mission_order_type not in [MissionOrder.option_vanilla_shuffled, MissionOrder.option_grid] - or ( - mission_order_type == MissionOrder.option_grid - and get_option_value(world, "maximum_campaign_size") < 20 - ) - ) + if (world.options.exclude_very_hard_missions == ExcludeVeryHardMissions.option_true) or ( + world.options.exclude_very_hard_missions == ExcludeVeryHardMissions.option_default + and mission_order_type in dynamic_mission_orders and world.options.maximum_campaign_size < 20 ): excluded_missions = excluded_missions.union( [mission for mission in SC2Mission if diff --git a/worlds/sc2/PoolFilter.py b/worlds/sc2/PoolFilter.py index 5986e7adf065..3f15745f1fc4 100644 --- a/worlds/sc2/PoolFilter.py +++ b/worlds/sc2/PoolFilter.py @@ -215,7 +215,7 @@ def num_missions(world: World) -> int: return len(misssions) - 1 # Menu else: mission_pools = filter_missions(world) - return sum(len(pool) for _, pool in mission_pools.items()) + return min(world.options.maximum_campaign_size, sum(len(pool) for _, pool in mission_pools.items())) class ValidInventory: From 35b2031155f6c13194ce71c9ac1529f9c3d86b42 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sun, 5 May 2024 17:16:54 -0400 Subject: [PATCH 08/24] Integer type fix Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py index e44eb57b1645..51a7d6c02744 100644 --- a/worlds/sc2/MissionOrders.py +++ b/worlds/sc2/MissionOrders.py @@ -287,7 +287,7 @@ def make_blitz(num_missions: int) -> list[FillMission]: min_width, max_width = 2, 5 mission_divisor = 5 dynamic_width = num_missions / mission_divisor - width = max(min(dynamic_width, max_width), min_width) + width = math.floor(max(min(dynamic_width, max_width), min_width)) middle_column = math.floor(width / 2) connections = [MissionConnection(-1)] mission_number = 0 From a2541f188f830a9480200565b2e78460d11f2393 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sun, 5 May 2024 17:37:15 -0400 Subject: [PATCH 09/24] Renamed diagonal to hopscotch and added option descriptions Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 2 +- worlds/sc2/Options.py | 13 +++++++------ worlds/sc2/Regions.py | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py index 51a7d6c02744..ac3de4f25e4d 100644 --- a/worlds/sc2/MissionOrders.py +++ b/worlds/sc2/MissionOrders.py @@ -320,7 +320,7 @@ def make_blitz(num_missions: int) -> list[FillMission]: return {SC2Campaign.GLOBAL: mission_order} -def make_diagonal(two_start_positions: bool, num_missions: int): +def make_hopscotch(two_start_positions: bool, num_missions: int): mission_order: List[FillMission] = [] menu_connection = [MissionConnection(-1)] max_width = 7 diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index 69c4dffd88c6..4bcc8b423ed0 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -64,8 +64,9 @@ class MissionOrder(Choice): Mini Campaign (47 total if all campaigns enabled): Shorter version of the campaign with randomized missions and optional branches. Blitz: Missions are divided into sets. Complete one mission from a set to advance to the next set. Gauntlet: A linear path of missions to complete the campaign. - Grid: A grid that will resize to use all non-excluded missions. Corners may be omitted to make the grid more square. Complete the bottom-right mission to win. - Golden Path: + Grid: Missions are arranged into a grid. Completing a mission unlocks the adjacent missions. Corners may be omitted to make the grid more square. Complete the bottom-right mission to win. + Golden Path: A required line of missions with several optional branches, similar to the Wings of Liberty campaign. + Hopscotch: Missions alternate between mandatory missions and pairs of optional missions. """ display_name = "Mission Order" option_vanilla = 0 @@ -75,7 +76,7 @@ class MissionOrder(Choice): option_gauntlet = 6 option_grid = 9 option_golden_path = 10 - option_diagonal = 11 + option_hopscotch = 11 class MaximumCampaignSize(Range): @@ -91,8 +92,8 @@ class MaximumCampaignSize(Range): class GridTwoStartPositions(Toggle): """ - If turned on and 'grid' mission order is selected, removes a mission from the starting - corner sets the adjacent two missions as the starter missions. + If turned on and 'grid' or 'hopscotch' mission orders are selected, + removes a mission from the starting corner and sets the adjacent two missions as the starter missions. """ display_name = "Start with two unlocked missions on grid" default = Toggle.option_false @@ -904,7 +905,7 @@ def get_excluded_missions(world: World) -> Set[SC2Mission]: MissionOrder.option_grid, MissionOrder.option_gauntlet, MissionOrder.option_blitz, - MissionOrder.option_diagonal + MissionOrder.option_hopscotch ] kerrigan_unit_available = [ diff --git a/worlds/sc2/Regions.py b/worlds/sc2/Regions.py index cc7d26db7d9a..fda07dff0942 100644 --- a/worlds/sc2/Regions.py +++ b/worlds/sc2/Regions.py @@ -7,7 +7,7 @@ GridTwoStartPositions, static_mission_orders, dynamic_mission_orders from .MissionTables import MissionInfo, vanilla_mission_req_table, \ MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection -from .MissionOrders import make_gauntlet, make_blitz, make_golden_path, make_diagonal +from .MissionOrders import make_gauntlet, make_blitz, make_golden_path, make_hopscotch from .PoolFilter import filter_missions from worlds.AutoWorld import World @@ -394,8 +394,8 @@ def make_dynamic_mission_order( return make_gauntlet(num_missions) elif mission_order_type == MissionOrder.option_blitz: return make_blitz(num_missions) - elif mission_order_type == MissionOrder.option_diagonal: - return make_diagonal(world.options.grid_two_start_positions, num_missions) + elif mission_order_type == MissionOrder.option_hopscotch: + return make_hopscotch(world.options.grid_two_start_positions, num_missions) def create_structured_regions( From 90e0730d30e6f336e1c05b763a1ed5a455873b16 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Mon, 6 May 2024 06:53:28 -0400 Subject: [PATCH 10/24] Removed extra line created by merge Signed-off-by: Magnemania --- worlds/sc2/PoolFilter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/worlds/sc2/PoolFilter.py b/worlds/sc2/PoolFilter.py index 8d0dd5929bda..78b16b27b586 100644 --- a/worlds/sc2/PoolFilter.py +++ b/worlds/sc2/PoolFilter.py @@ -76,7 +76,6 @@ def filter_missions(world: World) -> Dict[MissionPools, List[SC2Mission]]: goal_priorities = {campaign: get_campaign_goal_priority(campaign, excluded_missions) for campaign in enabled_campaigns} goal_level = max(goal_priorities.values()) candidate_campaigns: List[SC2Campaign] = [campaign for campaign, goal_priority in goal_priorities.items() if goal_priority == goal_level] - candidate_campaigns.sort(key=lambda it: it.id) candidate_campaigns.sort(key=lambda it: it.id) goal_campaign = world.random.choice(candidate_campaigns) From e7b229498a8ff7f5b139e5d6e3f70b28d42a45c9 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Mon, 6 May 2024 07:19:44 -0400 Subject: [PATCH 11/24] I love supporting python 3.8 Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py index ac3de4f25e4d..3e1513d4cd16 100644 --- a/worlds/sc2/MissionOrders.py +++ b/worlds/sc2/MissionOrders.py @@ -180,7 +180,7 @@ def mini_campaign_order() -> Dict[SC2Campaign, List[FillMission]]: max_difficulty = len(smooth_difficulty) - 1 -def make_golden_path(world: World, num_missions: int) -> list[FillMission]: +def make_golden_path(world: World, num_missions: int) -> List[FillMission]: chain_name_options = ['Mar Sara', 'Char', 'Kaldir', 'Zerus', 'Skygeirr Station', 'Dominion Space', 'Korhal', 'Aiur', 'Shakuras', 'Ulnar'] world.random.shuffle(chain_name_options) @@ -247,8 +247,8 @@ def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): return {SC2Campaign.GLOBAL: campaign.mission_order} -def make_gauntlet(num_missions: int) -> list[FillMission]: - mission_order: list[FillMission] = [] +def make_gauntlet(num_missions: int) -> List[FillMission]: + mission_order: List[FillMission] = [] row_length = 7 rows = math.ceil(num_missions / row_length) difficulty_rate = (max_difficulty + 1) / num_missions if num_missions < 21 else 1/3 @@ -283,7 +283,7 @@ def make_gauntlet(num_missions: int) -> list[FillMission]: return {SC2Campaign.GLOBAL: mission_order} -def make_blitz(num_missions: int) -> list[FillMission]: +def make_blitz(num_missions: int) -> List[FillMission]: min_width, max_width = 2, 5 mission_divisor = 5 dynamic_width = num_missions / mission_divisor From c97399600fddc9ee89162960f6f5c663bee880aa Mon Sep 17 00:00:00 2001 From: Magnemania Date: Mon, 6 May 2024 08:55:45 -0400 Subject: [PATCH 12/24] Increment slot data version, changed client to look for old grids on old data Signed-off-by: Magnemania --- worlds/sc2/Client.py | 2 +- worlds/sc2/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/sc2/Client.py b/worlds/sc2/Client.py index 7490c36971c6..cad93f6ec1c7 100644 --- a/worlds/sc2/Client.py +++ b/worlds/sc2/Client.py @@ -1365,7 +1365,7 @@ def mission_reqs_completed(ctx: SC2Context, mission_name: str, missions_complete req_success = False # Grid and Blitz-specific logic (to avoid long path checks and infinite recursion) (Includes legacy grids) - if ctx.mission_order in (MissionOrder.option_grid, MissionOrder.option_blitz, 3, 4, 8): + if ctx.mission_order in (MissionOrder.option_grid, MissionOrder.option_blitz) or (ctx.slot_data_version <= 3 and ctx.mission_order in (3, 4, 8)): if req_success: return True else: diff --git a/worlds/sc2/__init__.py b/worlds/sc2/__init__.py index 59c6fe900197..9612a8694637 100644 --- a/worlds/sc2/__init__.py +++ b/worlds/sc2/__init__.py @@ -118,7 +118,7 @@ def fill_slot_data(self): slot_data["nova_covert_ops_only"] = (enabled_campaigns == {SC2Campaign.NCO}) slot_data["mission_req"] = slot_req_table slot_data["final_mission"] = self.final_mission_id - slot_data["version"] = 3 + slot_data["version"] = 4 if SC2Campaign.HOTS not in enabled_campaigns: slot_data["kerrigan_presence"] = KerriganPresence.option_not_present From ebda116e68f82c8e0b14eaa2f1b6be8f96be3a5f Mon Sep 17 00:00:00 2001 From: Magnemania Date: Tue, 7 May 2024 07:28:45 -0400 Subject: [PATCH 13/24] Removed magic numbers from legacy grid orders Signed-off-by: Magnemania --- worlds/sc2/Client.py | 5 +++-- worlds/sc2/Options.py | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/worlds/sc2/Client.py b/worlds/sc2/Client.py index cad93f6ec1c7..b7afe71cce23 100644 --- a/worlds/sc2/Client.py +++ b/worlds/sc2/Client.py @@ -32,6 +32,7 @@ DisableForcedCamera, SkipCutscenes, GrantStoryTech, GrantStoryLevels, TakeOverAIAllies, RequiredTactics, SpearOfAdunPresence, SpearOfAdunPresentInNoBuild, SpearOfAdunAutonomouslyCastAbilityPresence, SpearOfAdunAutonomouslyCastPresentInNoBuild, MineralsPerItem, VespenePerItem, StartingSupplyPerItem, + LEGACY_GRID_ORDERS, ) @@ -1364,8 +1365,8 @@ def mission_reqs_completed(ctx: SC2Context, mission_name: str, missions_complete else: req_success = False - # Grid and Blitz-specific logic (to avoid long path checks and infinite recursion) (Includes legacy grids) - if ctx.mission_order in (MissionOrder.option_grid, MissionOrder.option_blitz) or (ctx.slot_data_version <= 3 and ctx.mission_order in (3, 4, 8)): + # Grid and Blitz-specific logic (to avoid long path checks and infinite recursion) + if ctx.mission_order in (MissionOrder.option_grid, MissionOrder.option_blitz) or (ctx.slot_data_version <= 3 and ctx.mission_order in LEGACY_GRID_ORDERS): if req_success: return True else: diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index 9ce196673873..337a19823329 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -908,6 +908,8 @@ def get_excluded_missions(world: World) -> Set[SC2Mission]: MissionOrder.option_hopscotch ] +LEGACY_GRID_ORDERS = {3, 4, 8} # Medium Grid, Mini Grid, and Tiny Grid respectively + kerrigan_unit_available = [ KerriganPresence.option_vanilla, ] \ No newline at end of file From b0e8f118c6e6896c9cb1d6afe8deea8eedad0599 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Tue, 7 May 2024 07:35:28 -0400 Subject: [PATCH 14/24] Type hint fixes Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 8 ++++---- worlds/sc2/Regions.py | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py index 3e1513d4cd16..c306dbc5d751 100644 --- a/worlds/sc2/MissionOrders.py +++ b/worlds/sc2/MissionOrders.py @@ -180,7 +180,7 @@ def mini_campaign_order() -> Dict[SC2Campaign, List[FillMission]]: max_difficulty = len(smooth_difficulty) - 1 -def make_golden_path(world: World, num_missions: int) -> List[FillMission]: +def make_golden_path(world: World, num_missions: int) -> Dict[SC2Campaign, List[FillMission]]: chain_name_options = ['Mar Sara', 'Char', 'Kaldir', 'Zerus', 'Skygeirr Station', 'Dominion Space', 'Korhal', 'Aiur', 'Shakuras', 'Ulnar'] world.random.shuffle(chain_name_options) @@ -247,7 +247,7 @@ def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): return {SC2Campaign.GLOBAL: campaign.mission_order} -def make_gauntlet(num_missions: int) -> List[FillMission]: +def make_gauntlet(num_missions: int) -> Dict[SC2Campaign, List[FillMission]]: mission_order: List[FillMission] = [] row_length = 7 rows = math.ceil(num_missions / row_length) @@ -283,7 +283,7 @@ def make_gauntlet(num_missions: int) -> List[FillMission]: return {SC2Campaign.GLOBAL: mission_order} -def make_blitz(num_missions: int) -> List[FillMission]: +def make_blitz(num_missions: int) -> Dict[SC2Campaign, List[FillMission]]: min_width, max_width = 2, 5 mission_divisor = 5 dynamic_width = num_missions / mission_divisor @@ -320,7 +320,7 @@ def make_blitz(num_missions: int) -> List[FillMission]: return {SC2Campaign.GLOBAL: mission_order} -def make_hopscotch(two_start_positions: bool, num_missions: int): +def make_hopscotch(two_start_positions: bool, num_missions: int) -> Dict[SC2Campaign, List[FillMission]]: mission_order: List[FillMission] = [] menu_connection = [MissionConnection(-1)] max_width = 7 diff --git a/worlds/sc2/Regions.py b/worlds/sc2/Regions.py index fda07dff0942..9945ed5760d0 100644 --- a/worlds/sc2/Regions.py +++ b/worlds/sc2/Regions.py @@ -6,7 +6,7 @@ from .Options import get_option_value, MissionOrder, get_enabled_campaigns, campaign_depending_orders, \ GridTwoStartPositions, static_mission_orders, dynamic_mission_orders from .MissionTables import MissionInfo, vanilla_mission_req_table, \ - MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection + MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection, FillMission from .MissionOrders import make_gauntlet, make_blitz, make_golden_path, make_hopscotch from .PoolFilter import filter_missions from worlds.AutoWorld import World @@ -27,7 +27,7 @@ def create_regions( * int The number of missions in the world * str The name of the goal location """ - mission_order_type: MissionOrder = get_option_value(world, "mission_order") + mission_order_type: MissionOrder = world.options.mission_order if mission_order_type == MissionOrder.option_vanilla: return create_vanilla_regions(world, locations, location_cache) @@ -379,8 +379,7 @@ def make_grid_connect_rule( def make_dynamic_mission_order( world: World, mission_order_type: int -) -> Tuple[Dict[SC2Campaign, Dict[str, MissionInfo]], int, str]: - player = world.player +) -> Dict[SC2Campaign, List[FillMission]]: mission_pools = filter_missions(world) mission_pool = [mission for mission_pool in mission_pools.values() for mission in mission_pool] From d2a3cbd38b3992a4d542931084cf870fde02a09f Mon Sep 17 00:00:00 2001 From: Magnemania Date: Tue, 7 May 2024 07:38:50 -0400 Subject: [PATCH 15/24] Variable renames in golden_path Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py index c306dbc5d751..6da8b0103882 100644 --- a/worlds/sc2/MissionOrders.py +++ b/worlds/sc2/MissionOrders.py @@ -187,21 +187,21 @@ def make_golden_path(world: World, num_missions: int) -> Dict[SC2Campaign, List[ first_chain = chain_name_options.pop() first_mission = FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.GLOBAL)], first_chain, - completion_critical=True) + completion_critical=True) class Campaign: - def __init__(self, first_mission: FillMission, missions_remaining: int): - self.mission_order = [first_mission] - self.counter = 0 + def __init__(self, root_mission: FillMission, missions_remaining: int): + self.mission_order = [root_mission] + self.mission_counter = 0 self.last_mission_in_chain = [0] - self.chain_names = [first_mission.category] + self.chain_names = [root_mission.category] self.missions_remaining = missions_remaining self.padding = 0 def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): if self.missions_remaining == 0 and difficulty is not MissionPools.FINAL: return - self.counter += 1 + self.mission_counter += 1 if self.mission_order[self.last_mission_in_chain[chain]].number == required_missions or required_missions <= 1: required_missions = 0 mission_connections = [MissionConnection(self.last_mission_in_chain[chain], SC2Campaign.GLOBAL)] @@ -221,19 +221,19 @@ def add_mission(self, chain: int, difficulty: int, required_missions: int = 0): completion_critical=chain == 0, ui_vertical_padding=padding )) - self.last_mission_in_chain[chain] = self.counter + self.last_mission_in_chain[chain] = self.mission_counter if chain == 0: self.padding += 1 self.missions_remaining -= 1 campaign = Campaign(first_mission, num_missions - 2) current_required_missions = 0 - main_chain = 0 + main_chain_length = 0 while campaign.missions_remaining > 0: - mission_difficulty = smooth_difficulty[min(main_chain, max_difficulty)] - main_chain += 1 - if main_chain % 2 == 1: # Adding branches - chains_to_make = 0 if len(campaign.chain_names) > 5 else 2 if main_chain == 1 else 1 + mission_difficulty = smooth_difficulty[min(main_chain_length, max_difficulty)] + main_chain_length += 1 + if main_chain_length % 2 == 1: # Adding branches + chains_to_make = 0 if len(campaign.chain_names) > 5 else 2 if main_chain_length == 1 else 1 for new_chain in range(chains_to_make): campaign.chain_names.append(chain_name_options.pop()) campaign.last_mission_in_chain.append(campaign.last_mission_in_chain[0]) From 4a59682a9557616b43d65fee2cdcb5d9f7f85e78 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Tue, 7 May 2024 07:43:35 -0400 Subject: [PATCH 16/24] Replaced floors with integer division Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py index 6da8b0103882..043353adc824 100644 --- a/worlds/sc2/MissionOrders.py +++ b/worlds/sc2/MissionOrders.py @@ -286,14 +286,14 @@ def make_gauntlet(num_missions: int) -> Dict[SC2Campaign, List[FillMission]]: def make_blitz(num_missions: int) -> Dict[SC2Campaign, List[FillMission]]: min_width, max_width = 2, 5 mission_divisor = 5 - dynamic_width = num_missions / mission_divisor - width = math.floor(max(min(dynamic_width, max_width), min_width)) - middle_column = math.floor(width / 2) + dynamic_width = num_missions // mission_divisor + width = max(min(dynamic_width, max_width), min_width) + middle_column = width // 2 connections = [MissionConnection(-1)] mission_number = 0 mission_order: List[FillMission] = [] if num_missions % width > middle_column: - final_row = math.floor(num_missions / width) * width + final_row = width * (num_missions // width) final_mission_number = final_row + middle_column else: final_mission_number = num_missions - 1 From 9fdaede201d9fa823ec5cba4503d7957e1b1a95b Mon Sep 17 00:00:00 2001 From: Magnemania Date: Tue, 7 May 2024 07:59:35 -0400 Subject: [PATCH 17/24] Added more golden path chain names Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py index 043353adc824..769d47a7399d 100644 --- a/worlds/sc2/MissionOrders.py +++ b/worlds/sc2/MissionOrders.py @@ -181,8 +181,11 @@ def mini_campaign_order() -> Dict[SC2Campaign, List[FillMission]]: def make_golden_path(world: World, num_missions: int) -> Dict[SC2Campaign, List[FillMission]]: - chain_name_options = ['Mar Sara', 'Char', 'Kaldir', 'Zerus', 'Skygeirr Station', - 'Dominion Space', 'Korhal', 'Aiur', 'Shakuras', 'Ulnar'] + chain_name_options = ['Mar Sara', 'Agria', 'Redstone', 'Meinhoff', 'Tarsonis', 'Char', + 'Umoja', 'Kaldir', 'Zerus', 'Skygeirr Station', 'Dominion Space', 'Korhal', + 'Aiur', 'Glacius', 'Shakuras', 'Ulnar', 'Slayn', + 'Antiga', 'Braxis', 'Moria', 'Tyrador', 'Xil', 'Zhakul', + 'Azeroth', 'Crouton', 'Draenor', 'Sanctuary'] world.random.shuffle(chain_name_options) first_chain = chain_name_options.pop() From fb56bdf4616c592b4884c3dfc40e73113f13ab03 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Wed, 8 May 2024 18:17:14 -0400 Subject: [PATCH 18/24] Remaining floor division Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py index 769d47a7399d..eb9bbd5798c3 100644 --- a/worlds/sc2/MissionOrders.py +++ b/worlds/sc2/MissionOrders.py @@ -302,7 +302,7 @@ def make_blitz(num_missions: int) -> Dict[SC2Campaign, List[FillMission]]: final_mission_number = num_missions - 1 while mission_number < num_missions: column = mission_number % width - row = math.floor(mission_number / width) + row = mission_number // width if mission_number == middle_column: difficulty = MissionPools.STARTER elif mission_number == final_mission_number: From 9570fd93a32853c8514a0f42b535de4404df6e11 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sat, 11 May 2024 12:47:47 -0400 Subject: [PATCH 19/24] Added mini campaign to default very hard exclusion list Signed-off-by: Magnemania --- worlds/sc2/Options.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index 337a19823329..b7f4baa6cb54 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -870,9 +870,11 @@ def get_excluded_missions(world: World) -> Set[SC2Mission]: excluded_missions: Set[SC2Mission] = set([lookup_name_to_mission[name] for name in excluded_mission_names]) # Excluding Very Hard missions depending on options - if (world.options.exclude_very_hard_missions == ExcludeVeryHardMissions.option_true) or ( - world.options.exclude_very_hard_missions == ExcludeVeryHardMissions.option_default - and mission_order_type in dynamic_mission_orders and world.options.maximum_campaign_size < 20 + if world.options.exclude_very_hard_missions == ExcludeVeryHardMissions.option_true or ( + world.options.exclude_very_hard_missions == ExcludeVeryHardMissions.option_default and ( + mission_order_type in dynamic_mission_orders and world.options.maximum_campaign_size < 20 or + mission_order_type == MissionOrder.option_mini_campaign + ) ): excluded_missions = excluded_missions.union( [mission for mission in SC2Mission if From b1a2883da103dca67ac95e64009c649ad762e538 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sat, 11 May 2024 13:00:32 -0400 Subject: [PATCH 20/24] Updated type hint Signed-off-by: Magnemania --- worlds/sc2/Regions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/sc2/Regions.py b/worlds/sc2/Regions.py index a378cfd158ba..2c0a3e601587 100644 --- a/worlds/sc2/Regions.py +++ b/worlds/sc2/Regions.py @@ -381,7 +381,7 @@ def make_grid_connect_rule( def make_dynamic_mission_order( - world: World, + world: 'SC2World', mission_order_type: int ) -> Dict[SC2Campaign, List[FillMission]]: mission_pools = filter_missions(world) From df15bce57ea10113c1667ed342d4434b4013fd89 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sat, 11 May 2024 13:26:19 -0400 Subject: [PATCH 21/24] Removed unnecessary import in merge Signed-off-by: Magnemania --- worlds/sc2/Options.py | 1 - 1 file changed, 1 deletion(-) diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py index ba3b06bc9d58..7b4e9b6f087b 100644 --- a/worlds/sc2/Options.py +++ b/worlds/sc2/Options.py @@ -8,7 +8,6 @@ from .MissionTables import SC2Campaign, SC2Mission, lookup_name_to_mission, MissionPools, get_no_build_missions, \ campaign_mission_table from .MissionOrders import vanilla_shuffle_order, mini_campaign_order -from worlds.AutoWorld import World if TYPE_CHECKING: from worlds.AutoWorld import World From 991aeca8947e28377b1d802a13cee5952811e105 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sat, 11 May 2024 13:28:50 -0400 Subject: [PATCH 22/24] Removed unnecessary method in merge Signed-off-by: Magnemania --- worlds/sc2/PoolFilter.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/worlds/sc2/PoolFilter.py b/worlds/sc2/PoolFilter.py index 331fccaa092e..003766a7413d 100644 --- a/worlds/sc2/PoolFilter.py +++ b/worlds/sc2/PoolFilter.py @@ -207,17 +207,6 @@ def copy_item(item: Item): return Item(item.name, item.classification, item.code, item.player) -def num_missions(world: 'SC2World') -> int: - mission_order_type = get_option_value(world, "mission_order") - if mission_order_type in static_mission_orders: - mission_order = static_mission_orders[mission_order_type]() - misssions = [mission for campaign in mission_order for mission in mission_order[campaign]] - return len(misssions) - 1 # Menu - else: - mission_pools = filter_missions(world) - return min(world.options.maximum_campaign_size, sum(len(pool) for _, pool in mission_pools.items())) - - class ValidInventory: def has(self, item: str, player: int): From 048f59f3608057c1068f5bb2ed3aebbf3047f019 Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sat, 11 May 2024 15:22:52 -0400 Subject: [PATCH 23/24] Fixed generation issue for starter unit no-build settings Signed-off-by: Magnemania --- worlds/sc2/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/worlds/sc2/__init__.py b/worlds/sc2/__init__.py index 837f0d3ddee4..1b446f855f1c 100644 --- a/worlds/sc2/__init__.py +++ b/worlds/sc2/__init__.py @@ -450,8 +450,7 @@ def flag_start_unit(world: SC2World, item_list: List[FilterItem], starter_unit: # If the first mission is a logic-less no-build missions = get_all_missions(world.mission_req_table) build_missions = [mission for mission in missions if MissionFlag.NoBuild not in mission.flags] - races = set(mission.race for mission in build_missions) - races.remove(SC2Race.ANY) + races = {mission.race for mission in build_missions if mission.race != SC2Race.ANY} if races: first_race = world.random.choice(list(races)) From 9894e5ddd09d141a32745d644b4b69b499a2773c Mon Sep 17 00:00:00 2001 From: Magnemania Date: Sat, 11 May 2024 15:23:16 -0400 Subject: [PATCH 24/24] Added Haven, Valhalla, and Chau Sara to golden path names (30 options) Signed-off-by: Magnemania --- worlds/sc2/MissionOrders.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/sc2/MissionOrders.py b/worlds/sc2/MissionOrders.py index eb9bbd5798c3..dbddffd38720 100644 --- a/worlds/sc2/MissionOrders.py +++ b/worlds/sc2/MissionOrders.py @@ -181,10 +181,10 @@ def mini_campaign_order() -> Dict[SC2Campaign, List[FillMission]]: def make_golden_path(world: World, num_missions: int) -> Dict[SC2Campaign, List[FillMission]]: - chain_name_options = ['Mar Sara', 'Agria', 'Redstone', 'Meinhoff', 'Tarsonis', 'Char', + chain_name_options = ['Mar Sara', 'Agria', 'Redstone', 'Meinhoff', 'Haven', 'Tarsonis', 'Valhalla', 'Char', 'Umoja', 'Kaldir', 'Zerus', 'Skygeirr Station', 'Dominion Space', 'Korhal', 'Aiur', 'Glacius', 'Shakuras', 'Ulnar', 'Slayn', - 'Antiga', 'Braxis', 'Moria', 'Tyrador', 'Xil', 'Zhakul', + 'Antiga', 'Braxis', 'Chau Sara', 'Moria', 'Tyrador', 'Xil', 'Zhakul', 'Azeroth', 'Crouton', 'Draenor', 'Sanctuary'] world.random.shuffle(chain_name_options)