mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
SC2: Greater variety on short generations (#1367)
Originally, short generations used an artificial cull to create balanced mission distributions. This resulted in campaigns that were somewhat too consistent, and on some standard settings combinations, this resulted in campaigns having The Outlaws as the second mission 100% of the time. It also caused generation to fail a bit too easily if the player excluded too many missions. This removes the cull and adds an additional early Easy mission slot to all of the reduced sized campaigns. When playing on No Build settings, this also pushes many of the missions down a difficulty level to ensure greater variety, and pushes additional missions down on Advanced Tactics. Additional small fixes: The in-world Excluded Missions validation check is replaced by the core OptionSet check. Fixed issue with Existing Items not getting their upgrades locked with Units Always Have Upgrades on.
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
from typing import NamedTuple, Dict, List, Set
|
||||
|
||||
from BaseClasses import MultiWorld
|
||||
from .Options import get_option_value
|
||||
from typing import NamedTuple, Dict, List
|
||||
from enum import IntEnum
|
||||
|
||||
no_build_regions_list = ["Liberation Day", "Breakout", "Ghost of a Chance", "Piercing the Shroud", "Whispers of Doom",
|
||||
"Belly of the Beast"]
|
||||
@@ -13,6 +11,14 @@ hard_regions_list = ["Maw of the Void", "Engine of Destruction", "In Utter Darkn
|
||||
"Shatter the Sky"]
|
||||
|
||||
|
||||
class MissionPools(IntEnum):
|
||||
STARTER = 0
|
||||
EASY = 1
|
||||
MEDIUM = 2
|
||||
HARD = 3
|
||||
FINAL = 4
|
||||
|
||||
|
||||
class MissionInfo(NamedTuple):
|
||||
id: int
|
||||
required_world: List[int]
|
||||
@@ -23,119 +29,119 @@ class MissionInfo(NamedTuple):
|
||||
|
||||
|
||||
class FillMission(NamedTuple):
|
||||
type: str
|
||||
type: int
|
||||
connect_to: List[int] # -1 connects to Menu
|
||||
category: str
|
||||
number: int = 0 # number of worlds need beaten
|
||||
completion_critical: bool = False # missions needed to beat game
|
||||
or_requirements: bool = False # true if the requirements should be or-ed instead of and-ed
|
||||
relegate: bool = False # true if this is a slot no build missions should be relegated to.
|
||||
removal_priority: int = 0 # how many missions missing from the pool required to remove this mission
|
||||
|
||||
|
||||
vanilla_shuffle_order = [
|
||||
FillMission("no_build", [-1], "Mar Sara", completion_critical=True),
|
||||
FillMission("easy", [0], "Mar Sara", completion_critical=True),
|
||||
FillMission("easy", [1], "Mar Sara", completion_critical=True),
|
||||
FillMission("easy", [2], "Colonist"),
|
||||
FillMission("medium", [3], "Colonist"),
|
||||
FillMission("hard", [4], "Colonist", number=7),
|
||||
FillMission("hard", [4], "Colonist", number=7, relegate=True),
|
||||
FillMission("easy", [2], "Artifact", completion_critical=True),
|
||||
FillMission("medium", [7], "Artifact", number=8, completion_critical=True),
|
||||
FillMission("hard", [8], "Artifact", number=11, completion_critical=True),
|
||||
FillMission("hard", [9], "Artifact", number=14, completion_critical=True),
|
||||
FillMission("hard", [10], "Artifact", completion_critical=True),
|
||||
FillMission("medium", [2], "Covert", number=4),
|
||||
FillMission("medium", [12], "Covert"),
|
||||
FillMission("hard", [13], "Covert", number=8, relegate=True),
|
||||
FillMission("hard", [13], "Covert", number=8, relegate=True),
|
||||
FillMission("medium", [2], "Rebellion", number=6),
|
||||
FillMission("hard", [16], "Rebellion"),
|
||||
FillMission("hard", [17], "Rebellion"),
|
||||
FillMission("hard", [18], "Rebellion"),
|
||||
FillMission("hard", [19], "Rebellion", relegate=True),
|
||||
FillMission("medium", [8], "Prophecy"),
|
||||
FillMission("hard", [21], "Prophecy"),
|
||||
FillMission("hard", [22], "Prophecy"),
|
||||
FillMission("hard", [23], "Prophecy", relegate=True),
|
||||
FillMission("hard", [11], "Char", completion_critical=True),
|
||||
FillMission("hard", [25], "Char", completion_critical=True),
|
||||
FillMission("hard", [25], "Char", completion_critical=True),
|
||||
FillMission("all_in", [26, 27], "Char", completion_critical=True, or_requirements=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "Mar Sara", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [0], "Mar Sara", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [1], "Mar Sara", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [2], "Colonist"),
|
||||
FillMission(MissionPools.MEDIUM, [3], "Colonist"),
|
||||
FillMission(MissionPools.HARD, [4], "Colonist", number=7),
|
||||
FillMission(MissionPools.HARD, [4], "Colonist", number=7, removal_priority=1),
|
||||
FillMission(MissionPools.EASY, [2], "Artifact", completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [7], "Artifact", number=8, completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [8], "Artifact", number=11, completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [9], "Artifact", number=14, completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [10], "Artifact", completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [2], "Covert", number=4),
|
||||
FillMission(MissionPools.MEDIUM, [12], "Covert"),
|
||||
FillMission(MissionPools.HARD, [13], "Covert", number=8, removal_priority=3),
|
||||
FillMission(MissionPools.HARD, [13], "Covert", number=8, removal_priority=2),
|
||||
FillMission(MissionPools.MEDIUM, [2], "Rebellion", number=6),
|
||||
FillMission(MissionPools.HARD, [16], "Rebellion"),
|
||||
FillMission(MissionPools.HARD, [17], "Rebellion"),
|
||||
FillMission(MissionPools.HARD, [18], "Rebellion"),
|
||||
FillMission(MissionPools.HARD, [19], "Rebellion", removal_priority=5),
|
||||
FillMission(MissionPools.MEDIUM, [8], "Prophecy", removal_priority=9),
|
||||
FillMission(MissionPools.HARD, [21], "Prophecy", removal_priority=8),
|
||||
FillMission(MissionPools.HARD, [22], "Prophecy", removal_priority=7),
|
||||
FillMission(MissionPools.HARD, [23], "Prophecy", removal_priority=6),
|
||||
FillMission(MissionPools.HARD, [11], "Char", completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [25], "Char", completion_critical=True, removal_priority=4),
|
||||
FillMission(MissionPools.HARD, [25], "Char", completion_critical=True),
|
||||
FillMission(MissionPools.FINAL, [26, 27], "Char", completion_critical=True, or_requirements=True)
|
||||
]
|
||||
|
||||
mini_campaign_order = [
|
||||
FillMission("no_build", [-1], "Mar Sara", completion_critical=True),
|
||||
FillMission("easy", [0], "Colonist"),
|
||||
FillMission("medium", [1], "Colonist"),
|
||||
FillMission("medium", [0], "Artifact", completion_critical=True),
|
||||
FillMission("medium", [3], "Artifact", number=4, completion_critical=True),
|
||||
FillMission("hard", [4], "Artifact", number=8, completion_critical=True),
|
||||
FillMission("medium", [0], "Covert", number=2),
|
||||
FillMission("hard", [6], "Covert"),
|
||||
FillMission("medium", [0], "Rebellion", number=3),
|
||||
FillMission("hard", [8], "Rebellion"),
|
||||
FillMission("medium", [4], "Prophecy"),
|
||||
FillMission("hard", [10], "Prophecy"),
|
||||
FillMission("hard", [5], "Char", completion_critical=True),
|
||||
FillMission("hard", [5], "Char", completion_critical=True),
|
||||
FillMission("all_in", [12, 13], "Char", completion_critical=True, or_requirements=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "Mar Sara", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [0], "Colonist"),
|
||||
FillMission(MissionPools.MEDIUM, [1], "Colonist"),
|
||||
FillMission(MissionPools.EASY, [0], "Artifact", completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [3], "Artifact", number=4, completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [4], "Artifact", number=8, completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [0], "Covert", number=2),
|
||||
FillMission(MissionPools.HARD, [6], "Covert"),
|
||||
FillMission(MissionPools.MEDIUM, [0], "Rebellion", number=3),
|
||||
FillMission(MissionPools.HARD, [8], "Rebellion"),
|
||||
FillMission(MissionPools.MEDIUM, [4], "Prophecy"),
|
||||
FillMission(MissionPools.HARD, [10], "Prophecy"),
|
||||
FillMission(MissionPools.HARD, [5], "Char", completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [5], "Char", completion_critical=True),
|
||||
FillMission(MissionPools.FINAL, [12, 13], "Char", completion_critical=True, or_requirements=True)
|
||||
]
|
||||
|
||||
gauntlet_order = [
|
||||
FillMission("no_build", [-1], "I", completion_critical=True),
|
||||
FillMission("easy", [0], "II", completion_critical=True),
|
||||
FillMission("medium", [1], "III", completion_critical=True),
|
||||
FillMission("medium", [2], "IV", completion_critical=True),
|
||||
FillMission("hard", [3], "V", completion_critical=True),
|
||||
FillMission("hard", [4], "VI", completion_critical=True),
|
||||
FillMission("all_in", [5], "Final", completion_critical=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "I", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [0], "II", completion_critical=True),
|
||||
FillMission(MissionPools.EASY, [1], "III", completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [2], "IV", completion_critical=True),
|
||||
FillMission(MissionPools.MEDIUM, [3], "V", completion_critical=True),
|
||||
FillMission(MissionPools.HARD, [4], "VI", completion_critical=True),
|
||||
FillMission(MissionPools.FINAL, [5], "Final", completion_critical=True)
|
||||
]
|
||||
|
||||
grid_order = [
|
||||
FillMission("no_build", [-1], "_1"),
|
||||
FillMission("medium", [0], "_1"),
|
||||
FillMission("medium", [1, 6, 3], "_1", or_requirements=True),
|
||||
FillMission("hard", [2, 7], "_1", or_requirements=True),
|
||||
FillMission("easy", [0], "_2"),
|
||||
FillMission("medium", [1, 4], "_2", or_requirements=True),
|
||||
FillMission("hard", [2, 5, 10, 7], "_2", or_requirements=True),
|
||||
FillMission("hard", [3, 6, 11], "_2", or_requirements=True),
|
||||
FillMission("medium", [4, 9, 12], "_3", or_requirements=True),
|
||||
FillMission("hard", [5, 8, 10, 13], "_3", or_requirements=True),
|
||||
FillMission("hard", [6, 9, 11, 14], "_3", or_requirements=True),
|
||||
FillMission("hard", [7, 10], "_3", or_requirements=True),
|
||||
FillMission("hard", [8, 13], "_4", or_requirements=True),
|
||||
FillMission("hard", [9, 12, 14], "_4", or_requirements=True),
|
||||
FillMission("hard", [10, 13], "_4", or_requirements=True),
|
||||
FillMission("all_in", [11, 14], "_4", or_requirements=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "_1"),
|
||||
FillMission(MissionPools.EASY, [0], "_1"),
|
||||
FillMission(MissionPools.MEDIUM, [1, 6, 3], "_1", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [2, 7], "_1", or_requirements=True),
|
||||
FillMission(MissionPools.EASY, [0], "_2"),
|
||||
FillMission(MissionPools.MEDIUM, [1, 4], "_2", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [2, 5, 10, 7], "_2", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [3, 6, 11], "_2", or_requirements=True),
|
||||
FillMission(MissionPools.MEDIUM, [4, 9, 12], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [5, 8, 10, 13], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [6, 9, 11, 14], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [7, 10], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [8, 13], "_4", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [9, 12, 14], "_4", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [10, 13], "_4", or_requirements=True),
|
||||
FillMission(MissionPools.FINAL, [11, 14], "_4", or_requirements=True)
|
||||
]
|
||||
|
||||
mini_grid_order = [
|
||||
FillMission("no_build", [-1], "_1"),
|
||||
FillMission("medium", [0], "_1"),
|
||||
FillMission("medium", [1, 5], "_1", or_requirements=True),
|
||||
FillMission("easy", [0], "_2"),
|
||||
FillMission("medium", [1, 3], "_2", or_requirements=True),
|
||||
FillMission("hard", [2, 4], "_2", or_requirements=True),
|
||||
FillMission("medium", [3, 7], "_3", or_requirements=True),
|
||||
FillMission("hard", [4, 6], "_3", or_requirements=True),
|
||||
FillMission("all_in", [5, 7], "_3", or_requirements=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "_1"),
|
||||
FillMission(MissionPools.EASY, [0], "_1"),
|
||||
FillMission(MissionPools.MEDIUM, [1, 5], "_1", or_requirements=True),
|
||||
FillMission(MissionPools.EASY, [0], "_2"),
|
||||
FillMission(MissionPools.MEDIUM, [1, 3], "_2", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [2, 4], "_2", or_requirements=True),
|
||||
FillMission(MissionPools.MEDIUM, [3, 7], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [4, 6], "_3", or_requirements=True),
|
||||
FillMission(MissionPools.FINAL, [5, 7], "_3", or_requirements=True)
|
||||
]
|
||||
|
||||
blitz_order = [
|
||||
FillMission("no_build", [-1], "I"),
|
||||
FillMission("easy", [-1], "I"),
|
||||
FillMission("medium", [0, 1], "II", number=1, or_requirements=True),
|
||||
FillMission("medium", [0, 1], "II", number=1, or_requirements=True),
|
||||
FillMission("medium", [0, 1], "III", number=2, or_requirements=True),
|
||||
FillMission("medium", [0, 1], "III", number=2, or_requirements=True),
|
||||
FillMission("hard", [0, 1], "IV", number=3, or_requirements=True),
|
||||
FillMission("hard", [0, 1], "IV", number=3, or_requirements=True),
|
||||
FillMission("hard", [0, 1], "V", number=4, or_requirements=True),
|
||||
FillMission("hard", [0, 1], "V", number=4, or_requirements=True),
|
||||
FillMission("hard", [0, 1], "Final", number=5, or_requirements=True),
|
||||
FillMission("all_in", [0, 1], "Final", number=5, or_requirements=True)
|
||||
FillMission(MissionPools.STARTER, [-1], "I"),
|
||||
FillMission(MissionPools.EASY, [-1], "I"),
|
||||
FillMission(MissionPools.MEDIUM, [0, 1], "II", number=1, or_requirements=True),
|
||||
FillMission(MissionPools.MEDIUM, [0, 1], "II", number=1, or_requirements=True),
|
||||
FillMission(MissionPools.MEDIUM, [0, 1], "III", number=2, or_requirements=True),
|
||||
FillMission(MissionPools.MEDIUM, [0, 1], "III", number=2, or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [0, 1], "IV", number=3, or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [0, 1], "IV", number=3, or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [0, 1], "V", number=4, or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [0, 1], "V", number=4, or_requirements=True),
|
||||
FillMission(MissionPools.HARD, [0, 1], "Final", number=5, or_requirements=True),
|
||||
FillMission(MissionPools.FINAL, [0, 1], "Final", number=5, or_requirements=True)
|
||||
]
|
||||
|
||||
mission_orders = [vanilla_shuffle_order, vanilla_shuffle_order, mini_campaign_order, grid_order, mini_grid_order, blitz_order, gauntlet_order]
|
||||
@@ -176,40 +182,21 @@ vanilla_mission_req_table = {
|
||||
lookup_id_to_mission: Dict[int, str] = {
|
||||
data.id: mission_name for mission_name, data in vanilla_mission_req_table.items() if data.id}
|
||||
|
||||
no_build_starting_mission_locations = {
|
||||
starting_mission_locations = {
|
||||
"Liberation Day": "Liberation Day: Victory",
|
||||
"Breakout": "Breakout: Victory",
|
||||
"Ghost of a Chance": "Ghost of a Chance: Victory",
|
||||
"Piercing the Shroud": "Piercing the Shroud: Victory",
|
||||
"Whispers of Doom": "Whispers of Doom: Victory",
|
||||
"Belly of the Beast": "Belly of the Beast: Victory",
|
||||
}
|
||||
|
||||
build_starting_mission_locations = {
|
||||
"Zero Hour": "Zero Hour: First Group Rescued",
|
||||
"Evacuation": "Evacuation: First Chysalis",
|
||||
"Devil's Playground": "Devil's Playground: Tosh's Miners"
|
||||
}
|
||||
|
||||
advanced_starting_mission_locations = {
|
||||
"Devil's Playground": "Devil's Playground: Tosh's Miners",
|
||||
"Smash and Grab": "Smash and Grab: First Relic",
|
||||
"The Great Train Robbery": "The Great Train Robbery: North Defiler"
|
||||
}
|
||||
|
||||
|
||||
def get_starting_mission_locations(multiworld: MultiWorld, player: int) -> Set[str]:
|
||||
if get_option_value(multiworld, player, 'shuffle_no_build') or get_option_value(multiworld, player, 'mission_order') < 2:
|
||||
# Always start with a no-build mission unless explicitly relegating them
|
||||
# Vanilla and Vanilla Shuffled always start with a no-build even when relegated
|
||||
return no_build_starting_mission_locations
|
||||
elif get_option_value(multiworld, player, 'required_tactics') > 0:
|
||||
# Advanced Tactics/No Logic add more starting missions to the pool
|
||||
return {**build_starting_mission_locations, **advanced_starting_mission_locations}
|
||||
else:
|
||||
# Standard starting missions when relegate is on
|
||||
return build_starting_mission_locations
|
||||
|
||||
|
||||
alt_final_mission_locations = {
|
||||
"Maw of the Void": "Maw of the Void: Victory",
|
||||
"Engine of Destruction": "Engine of Destruction: Victory",
|
||||
|
||||
Reference in New Issue
Block a user