Skip to content

Commit

Permalink
Merge pull request ArchipelagoMW#154 from Salzkorn/sc2-next
Browse files Browse the repository at this point in the history
Additional Kerrigan level options & Levels per Mission Completed fix
  • Loading branch information
Ziktofel authored Feb 1, 2024
2 parents 503d574 + 2ff0f2f commit 7eaae57
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 20 deletions.
30 changes: 19 additions & 11 deletions worlds/sc2/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
from worlds.sc2.Options import MissionOrder, KerriganPrimalStatus, kerrigan_unit_available, KerriganPresence, \
GameSpeed, GenericUpgradeItems, GenericUpgradeResearch, ColorChoice, GenericUpgradeMissions, \
LocationInclusion, ExtraLocations, MasteryLocations, ChallengeLocations, VanillaLocations, \
DisableForcedCamera, SkipCutscenes, GrantStoryTech, TakeOverAIAllies, RequiredTactics, SpearOfAdunPresence, \
SpearOfAdunPresentInNoBuild, SpearOfAdunAutonomouslyCastAbilityPresence, \
DisableForcedCamera, SkipCutscenes, GrantStoryTech, GrantStoryLevels, TakeOverAIAllies, RequiredTactics, \
SpearOfAdunPresence, SpearOfAdunPresentInNoBuild, SpearOfAdunAutonomouslyCastAbilityPresence, \
SpearOfAdunAutonomouslyCastPresentInNoBuild


Expand Down Expand Up @@ -459,7 +459,10 @@ def on_package(self, cmd: str, args: dict) -> None:
self.kerrigan_presence = args["slot_data"].get("kerrigan_presence", KerriganPresence.option_vanilla)
self.kerrigan_primal_status = args["slot_data"].get("kerrigan_primal_status", KerriganPrimalStatus.option_vanilla)
self.kerrigan_levels_per_mission_completed = args["slot_data"].get("kerrigan_levels_per_mission_completed", 0)
self.kerrigan_levels_per_mission_completed_cap = args["slot_data"].get("kerrigan_levels_per_mission_completed_cap", -1)
self.kerrigan_total_level_cap = args["slot_data"].get("kerrigan_total_level_cap", -1)
self.grant_story_tech = args["slot_data"].get("grant_story_tech", GrantStoryTech.option_false)
self.grant_story_levels = args["slot_data"].get("grant_story_levels", GrantStoryLevels.option_additive)
self.required_tactics = args["slot_data"].get("required_tactics", RequiredTactics.option_standard)
self.take_over_ai_allies = args["slot_data"].get("take_over_ai_allies", TakeOverAIAllies.option_false)
self.spear_of_adun_presence = args["slot_data"].get("spear_of_adun_presence", SpearOfAdunPresence.option_not_present)
Expand All @@ -472,8 +475,9 @@ def on_package(self, cmd: str, args: dict) -> None:
self.nova_covert_ops_only = args["slot_data"].get("nova_covert_ops_only", False)

if self.required_tactics == RequiredTactics.option_no_logic:
# Locking Grant Story Tech if no logic
# Locking Grant Story Tech/Levels if no logic
self.grant_story_tech = GrantStoryTech.option_true
self.grant_story_levels = GrantStoryLevels.option_minimum

self.location_inclusions = {
LocationType.VICTORY: LocationInclusion.option_enabled, # Victory checks are always enabled
Expand Down Expand Up @@ -734,9 +738,14 @@ def calc_difficulty(difficulty: int):


def get_kerrigan_level(ctx: SC2Context, items: typing.Dict[SC2Race, typing.List[int]], missions_beaten: int) -> int:
value = items[SC2Race.ZERG][type_flaggroups[SC2Race.ZERG]["Level"]]
value += missions_beaten * ctx.kerrigan_levels_per_mission_completed
return value
item_value = items[SC2Race.ZERG][type_flaggroups[SC2Race.ZERG]["Level"]]
mission_value = missions_beaten * ctx.kerrigan_levels_per_mission_completed
if ctx.kerrigan_levels_per_mission_completed_cap != -1:
mission_value = min(mission_value, ctx.kerrigan_levels_per_mission_completed_cap)
total_value = item_value + mission_value
if ctx.kerrigan_total_level_cap != -1:
total_value = min(total_value, ctx.kerrigan_total_level_cap)
return total_value


def calculate_kerrigan_options(ctx: SC2Context) -> int:
Expand Down Expand Up @@ -869,7 +878,7 @@ async def on_step(self, iteration: int):
game_speed = self.ctx.game_speed_override
else:
game_speed = self.ctx.game_speed
await self.chat_send("?SetOptions {} {} {} {} {} {} {} {} {} {} {} {}".format(
await self.chat_send("?SetOptions {} {} {} {} {} {} {} {} {} {} {} {} {}".format(
difficulty,
self.ctx.generic_upgrade_research,
self.ctx.all_in_choice,
Expand All @@ -881,7 +890,8 @@ async def on_step(self, iteration: int):
self.ctx.take_over_ai_allies,
soa_options,
self.ctx.mission_order,
1 if self.ctx.nova_covert_ops_only else 0
1 if self.ctx.nova_covert_ops_only else 0,
self.ctx.grant_story_levels
))
await self.chat_send("?GiveResources {} {} {}".format(
start_items[SC2Race.ANY][0],
Expand Down Expand Up @@ -949,8 +959,6 @@ async def on_step(self, iteration: int):

for x, completed in enumerate(self.boni):
if not completed and game_state & (1 << (x + 2)):
# Store check amount ahead of time to avoid server changing value mid-calculation
checks = len(self.ctx.checked_locations)
await self.ctx.send_msgs(
[{"cmd": 'LocationChecks',
"locations": [get_location_offset(self.mission_id) + VICTORY_MODULO * self.mission_id + x + 1]}])
Expand All @@ -959,7 +967,7 @@ async def on_step(self, iteration: int):
await self.chat_send("?SendMessage LostConnection - Lost connection to game.")

def missions_beaten_count(self):
return len([location for location in self.ctx.locations_checked if location % VICTORY_MODULO == 0])
return len([location for location in self.ctx.checked_locations if location % VICTORY_MODULO == 0])

async def updateColors(self):
await self.chat_send("?SetColor rr " + str(self.ctx.player_color_raynor))
Expand Down
67 changes: 64 additions & 3 deletions worlds/sc2/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,13 +395,30 @@ class KerriganLevelsPerMissionCompleted(Range):
default = 0


class KerriganLevelsPerMissionCompletedCap(Range):
"""
Limits how many total levels Kerrigan can gain from beating missions. This does not affect levels gained from items.
Set to -1 to disable this limit.
NOTE: The following missions have these level requirements:
Supreme: 35
The Infinite Cycle: 70
See Grant Story Levels for more details.
"""
display_name = "Levels Per Mission Beaten Cap"
range_start = -1
range_end = 140
default = -1


class KerriganLevelItemSum(Range):
"""
Determines the sum of the level items in the world. This does not affect levels gained from checks.
Determines the sum of the level items in the world. This does not affect levels gained from beating missions.
NOTE: The following missions have these level requirements:
Supreme: 35
The Infinite Cycle: 70
See Grant Story Levels for more details.
"""
display_name = "Kerrigan Level Item Sum"
range_start = 0
Expand All @@ -415,7 +432,7 @@ class KerriganLevelItemDistribution(Choice):
Vanilla: Uses the distribution in the vanilla campaign.
This entails 32 individual levels and 6 packs of varying sizes.
This distribution always adds up to 70, ignoring the Level Item Sum setting.
Smooth: Uses a custom, condensed distribution of items between sizes 4 and 10,
Smooth: Uses a custom, condensed distribution of 10 items between sizes 4 and 10,
intended to fit more levels into settings with little room for filler while keeping some variance in level gains.
This distribution always adds up to 70, ignoring the Level Item Sum setting.
Size 70: Uses items worth 70 levels each.
Expand All @@ -441,6 +458,22 @@ class KerriganLevelItemDistribution(Choice):
default = option_smooth


class KerriganTotalLevelCap(Range):
"""
Limits how many total levels Kerrigan can gain from any source. Depending on your other settings,
there may be more levels available in the world, but they will not affect Kerrigan.
Set to -1 to disable this limit.
NOTE: The following missions have these level requirements:
Supreme: 35
The Infinite Cycle: 70
See Grant Story Levels for more details.
"""
display_name = "Total Level Cap"
range_start = -1
range_end = 140
default = -1


class StartPrimaryAbilities(Range):
"""Number of Primary Abilities (Kerrigan Tier 1, 2, and 4) to start the game with.
Expand All @@ -453,7 +486,7 @@ class StartPrimaryAbilities(Range):

class KerriganPrimalStatus(Choice):
"""Determines when Kerrigan appears in her Primal Zerg form.
This halves her maximum energy, but greatly increases her energy regeneration.
This greatly increases her energy regeneration.
Vanilla: Kerrigan is human in missions that canonically appear before The Crucible,
and zerg thereafter.
Expand Down Expand Up @@ -555,6 +588,31 @@ class GrantStoryTech(Toggle):
display_name = "Grant Story Tech"


class GrantStoryLevels(Choice):
"""
If enabled, grants Kerrigan the required minimum levels for the following missions:
Supreme: 35
The Infinite Cycle: 70
The bonus levels only apply during the listed missions, and can exceed the Total Level Cap.
If disabled, either of these missions is included, and there are not enough levels in the world, generation may fail.
To prevent this, either increase the amount of levels in the world, or enable this option.
If disabled and Required Tactics is set to no logic, this option is forced to Minimum.
Disabled: Kerrigan does not get bonus levels for these missions,
instead the levels must be gained from items or beating missions.
Additive: Kerrigan gains bonus levels equal to the mission's required level.
Minimum: Kerrigan is either at her real level, or at the mission's required level,
depending on which is higher.
"""
display_name = "Grant Story Levels"
option_disabled = 0
option_additive = 1
option_minimum = 2
default = option_minimum


class TakeOverAIAllies(Toggle):
"""
On maps supporting this feature allows you to take control over an AI Ally.
Expand Down Expand Up @@ -739,15 +797,18 @@ class Starcraft2Options(PerGameCommonOptions):
generic_upgrade_items: GenericUpgradeItems
kerrigan_presence: KerriganPresence
kerrigan_levels_per_mission_completed: KerriganLevelsPerMissionCompleted
kerrigan_levels_per_mission_completed_cap: KerriganLevelsPerMissionCompletedCap
kerrigan_level_item_sum: KerriganLevelItemSum
kerrigan_level_item_distribution: KerriganLevelItemDistribution
kerrigan_total_level_cap: KerriganTotalLevelCap
start_primary_abilities: StartPrimaryAbilities
kerrigan_primal_status: KerriganPrimalStatus
spear_of_adun_presence: SpearOfAdunPresence
spear_of_adun_present_in_no_build: SpearOfAdunPresentInNoBuild
spear_of_adun_autonomously_cast_ability_presence: SpearOfAdunAutonomouslyCastAbilityPresence
spear_of_adun_autonomously_cast_present_in_no_build: SpearOfAdunAutonomouslyCastPresentInNoBuild
grant_story_tech: GrantStoryTech
grant_story_levels: GrantStoryLevels
take_over_ai_allies: TakeOverAIAllies
locked_items: LockedItems
excluded_items: ExcludedItems
Expand Down
24 changes: 18 additions & 6 deletions worlds/sc2/Rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

from BaseClasses import MultiWorld, CollectionState
from .Options import get_option_value, RequiredTactics, kerrigan_unit_available, AllInMap, \
GrantStoryTech, TakeOverAIAllies, SpearOfAdunAutonomouslyCastAbilityPresence, get_enabled_campaigns, MissionOrder
GrantStoryTech, GrantStoryLevels, TakeOverAIAllies, SpearOfAdunAutonomouslyCastAbilityPresence, \
get_enabled_campaigns, MissionOrder
from .Items import get_basic_units, defense_ratings, zerg_defense_ratings, kerrigan_actives, air_defense_ratings, \
kerrigan_levels, get_full_item_list
from .MissionTables import SC2Race, SC2Campaign
Expand Down Expand Up @@ -431,18 +432,26 @@ def supreme_requirement(self, state: CollectionState) -> bool:
)

def kerrigan_levels(self, state: CollectionState, target: int) -> bool:
if self.story_tech_granted or not self.kerrigan_unit_available:
return True # Levels are granted by story tech
if self.kerrigan_levels_per_mission_completed > 0 and not self.is_item_placement(state):
if self.story_levels_granted or not self.kerrigan_unit_available:
return True # Levels are granted
if self.kerrigan_levels_per_mission_completed > 0 \
and self.kerrigan_levels_per_mission_completed_cap > 0 \
and not self.is_item_placement(state):
# Levels can be granted from mission completion.
# Item pool filtering isn't aware of missions beaten. Assume that missions beaten will fulfill this rule.
return True
levels = 0
levels += self.kerrigan_levels_per_mission_completed * state.count_group("Missions", self.player)
# Levels from missions beaten
levels = self.kerrigan_levels_per_mission_completed * state.count_group("Missions", self.player)
if self.kerrigan_levels_per_mission_completed_cap != -1:
levels = min(levels, self.kerrigan_levels_per_mission_completed_cap)
# Levels from items
for kerrigan_level_item in kerrigan_levels:
level_amount = get_full_item_list()[kerrigan_level_item].number
item_count = state.count(kerrigan_level_item, self.player)
levels += item_count * level_amount
# Total level cap
if self.kerrigan_total_level_cap != -1:
levels = min(levels, self.kerrigan_total_level_cap)

return levels >= target

Expand Down Expand Up @@ -928,7 +937,10 @@ def __init__(self, multiworld: MultiWorld, player: int):
self.kerrigan_unit_available = get_option_value(multiworld, self.player, 'kerrigan_presence') in kerrigan_unit_available \
and SC2Campaign.HOTS in get_enabled_campaigns(multiworld, self.player)
self.kerrigan_levels_per_mission_completed = get_option_value(multiworld, self.player, "kerrigan_levels_per_mission_completed")
self.kerrigan_levels_per_mission_completed_cap = get_option_value(multiworld, self.player, "kerrigan_levels_per_mission_completed_cap")
self.kerrigan_total_level_cap = get_option_value(multiworld, self.player, "kerrigan_total_level_cap")
self.story_tech_granted = get_option_value(multiworld, self.player, "grant_story_tech") == GrantStoryTech.option_true
self.story_levels_granted = get_option_value(multiworld, self.player, "grant_story_levels") != GrantStoryLevels.option_disabled
self.basic_terran_units = get_basic_units(self.multiworld, self.player, SC2Race.TERRAN)
self.basic_zerg_units = get_basic_units(self.multiworld, self.player, SC2Race.ZERG)
self.basic_protoss_units = get_basic_units(self.multiworld, self.player, SC2Race.PROTOSS)
Expand Down

0 comments on commit 7eaae57

Please sign in to comment.