Skip to content

Commit

Permalink
sc2: Adding an option to enable war council nerfs; added the first fe…
Browse files Browse the repository at this point in the history
…w War Council items
  • Loading branch information
MatthewMarinets committed Jul 2, 2024
1 parent 72e3de9 commit f09220f
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 29 deletions.
13 changes: 12 additions & 1 deletion worlds/sc2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from worlds.AutoWorld import WebWorld, World
from . import item_names
from .items import (
StarcraftItem, filler_items, get_full_item_list,
StarcraftItem, filler_items, get_full_item_list, ProtossItemType,
get_basic_units, ItemData, upgrade_included_names, kerrigan_actives, kerrigan_passives,
not_balanced_starting_units,
)
Expand Down Expand Up @@ -129,6 +129,7 @@ def create_items(self):
flag_start_inventory(self, item_list)
flag_unused_upgrade_types(self, item_list)
flag_user_excluded_item_sets(self, item_list)
flag_war_council_excludes(self, item_list)
flag_and_add_resource_locations(self, item_list)
pool: List[Item] = prune_item_pool(self, item_list)
pad_item_pool_with_filler(self, len(self.location_cache) - len(self.locked_locations) - len(pool), pool)
Expand Down Expand Up @@ -590,6 +591,16 @@ def flag_user_excluded_item_sets(world: SC2World, item_list: List[FilterItem]) -
item.flags |= ItemFilterFlags.Excluded
vanilla_nonprogressive_count[item.name] += 1

def flag_war_council_excludes(world: SC2World, item_list: List[FilterItem]) -> None:
"""Excludes items based on item set options (`only_vanilla_items`)"""
if world.options.allow_unit_nerfs:
return

for item in item_list:
if item.data.type != ProtossItemType.War_Council:
continue
item.flags |= ItemFilterFlags.Excluded


def flag_and_add_resource_locations(world: SC2World, item_list: List[FilterItem]) -> None:
"""
Expand Down
60 changes: 32 additions & 28 deletions worlds/sc2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
LocationInclusion, ExtraLocations, MasteryLocations, ChallengeLocations, VanillaLocations,
DisableForcedCamera, SkipCutscenes, GrantStoryTech, GrantStoryLevels, TakeOverAIAllies, RequiredTactics,
SpearOfAdunPresence, SpearOfAdunPresentInNoBuild, SpearOfAdunAutonomouslyCastAbilityPresence,
SpearOfAdunAutonomouslyCastPresentInNoBuild, LEGACY_GRID_ORDERS,
SpearOfAdunAutonomouslyCastPresentInNoBuild, AllowUnitNerfs, LEGACY_GRID_ORDERS,
)


Expand All @@ -49,12 +49,14 @@
from worlds._sc2common.bot.player import Bot
from worlds.sc2.items import (
lookup_id_to_name, get_full_item_list, ItemData,
race_to_item_type, upgrade_item_types, ZergItemType, upgrade_bundles, upgrade_included_names,
race_to_item_type, ZergItemType, ProtossItemType, upgrade_bundles, upgrade_included_names,
WEAPON_ARMOR_UPGRADE_MAX_LEVEL,
)
from worlds.sc2.locations import SC2WOL_LOC_ID_OFFSET, LocationType, SC2HOTS_LOC_ID_OFFSET
from worlds.sc2.mission_tables import lookup_id_to_mission, SC2Campaign, lookup_name_to_mission, \
from worlds.sc2.mission_tables import (
lookup_id_to_mission, SC2Campaign, lookup_name_to_mission,
lookup_id_to_campaign, MissionConnection, SC2Mission, campaign_mission_table, SC2Race
)
from worlds.sc2.regions import MissionInfo

import colorama
Expand Down Expand Up @@ -327,6 +329,7 @@ def _cmd_option(self, option_name: str = "", option_value: str = "") -> None:
ConfigurableOptionInfo('no_forced_camera', 'disable_forced_camera', options.DisableForcedCamera),
ConfigurableOptionInfo('skip_cutscenes', 'skip_cutscenes', options.SkipCutscenes),
ConfigurableOptionInfo('enable_morphling', 'enable_morphling', options.EnableMorphling, can_break_logic=True),
ConfigurableOptionInfo('unit_nerfs', 'allow_unit_nerfs', options.AllowUnitNerfs, can_break_logic=True),
)

WARNING_COLOUR = "salmon"
Expand Down Expand Up @@ -540,6 +543,7 @@ def __init__(self, *args, **kwargs) -> None:
self.kerrigan_presence: int = KerriganPresence.default
self.kerrigan_primal_status = 0
self.enable_morphling = EnableMorphling.default
self.allow_unit_nerfs: int = AllowUnitNerfs.default
self.mission_req_table: typing.Dict[SC2Campaign, typing.Dict[str, MissionInfo]] = {}
self.final_mission: int = 29
self.announcements: queue.Queue = queue.Queue()
Expand Down Expand Up @@ -647,6 +651,7 @@ def on_package(self, cmd: str, args: dict) -> None:
self.vespene_per_item = args["slot_data"].get("vespene_per_item", 15)
self.starting_supply_per_item = args["slot_data"].get("starting_supply_per_item", 2)
self.nova_covert_ops_only = args["slot_data"].get("nova_covert_ops_only", False)
self.allow_unit_nerfs = args["slot_data"].get("allow_unit_nerfs", AllowUnitNerfs.default)

if self.required_tactics == RequiredTactics.option_no_logic:
# Locking Grant Story Tech/Levels if no logic
Expand Down Expand Up @@ -890,6 +895,10 @@ def calculate_items(ctx: SC2Context) -> typing.Dict[SC2Race, typing.List[int]]:
shield_upgrade_item = item_list[item_names.PROGRESSIVE_PROTOSS_SHIELDS]
for _ in range(0, shield_upgrade_level):
accumulators[shield_upgrade_item.race][shield_upgrade_item.type.flag_word] += 1 << shield_upgrade_item.number

# War council option
if not ctx.allow_unit_nerfs:
accumulators[SC2Race.PROTOSS][ProtossItemType.War_Council] = (1 << 30) - 1

# Deprecated Orbital Command handling (Backwards compatibility):
if orbital_command_count > 0:
Expand Down Expand Up @@ -1103,23 +1112,24 @@ 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(
difficulty,
self.ctx.generic_upgrade_research,
self.ctx.all_in_choice,
game_speed,
self.ctx.disable_forced_camera,
self.ctx.skip_cutscenes,
kerrigan_options,
self.ctx.grant_story_tech,
self.ctx.take_over_ai_allies,
soa_options,
self.ctx.mission_order,
1 if self.ctx.nova_covert_ops_only else 0,
self.ctx.grant_story_levels,
self.ctx.enable_morphling,
mission_variant
))
await self.chat_send(
"?SetOptions"
f" {difficulty}"
f" {self.ctx.generic_upgrade_research}"
f" {self.ctx.all_in_choice}"
f" {game_speed}"
f" {self.ctx.disable_forced_camera}"
f" {self.ctx.skip_cutscenes}"
f" {kerrigan_options}"
f" {self.ctx.grant_story_tech}"
f" {self.ctx.take_over_ai_allies}"
f" {soa_options}"
f" {self.ctx.mission_order}"
f" {int(self.ctx.nova_covert_ops_only)}"
f" {self.ctx.grant_story_levels}"
f" {self.ctx.enable_morphling}"
f" {mission_variant}"
)
await self.chat_send("?GiveResources {} {} {}".format(
start_items[SC2Race.ANY][0],
start_items[SC2Race.ANY][1],
Expand Down Expand Up @@ -1220,17 +1230,11 @@ async def updateZergTech(self, current_items, kerrigan_level):
zerg_items = current_items[SC2Race.ZERG]
kerrigan_primal_by_items = kerrigan_primal(self.ctx, kerrigan_level)
kerrigan_primal_bot_value = 1 if kerrigan_primal_by_items else 0
await self.chat_send("?GiveZergTech {} {} {} {} {} {} {} {} {} {} {} {}".format(
kerrigan_level, kerrigan_primal_bot_value, zerg_items[0], zerg_items[1], zerg_items[2],
zerg_items[3], zerg_items[4], zerg_items[5], zerg_items[6], zerg_items[9], zerg_items[10], zerg_items[11]
))
await self.chat_send(f"?GiveZergTech {kerrigan_level} {kerrigan_primal_bot_value} " + ' '.join(map(str, zerg_items)))

async def updateProtossTech(self, current_items):
protoss_items = current_items[SC2Race.PROTOSS]
await self.chat_send("?GiveProtossTech {} {} {} {} {} {} {} {} {} {}".format(
protoss_items[0], protoss_items[1], protoss_items[2], protoss_items[3], protoss_items[4],
protoss_items[5], protoss_items[6], protoss_items[7], protoss_items[8], protoss_items[9]
))
await self.chat_send("?GiveProtossTech " + " ".join(map(str, protoss_items)))


def request_unfinished_missions(ctx: SC2Context) -> None:
Expand Down
8 changes: 8 additions & 0 deletions worlds/sc2/item_descriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
item_names.REAVER: (100, 100, 2),
DISPLAY_NAME_CLOAKED_ASSASSIN: (0, 50, 0),
item_names.SCOUT: (125, 25, 1),

# War Council
item_names.CENTURION: (0, 50, 0),
item_names.SENTINEL: (60, 0, 1),
}


Expand Down Expand Up @@ -830,6 +834,10 @@ def _ability_desc(unit_name_plural: str, ability_name: str, ability_description:
item_names.HAVOC_BLOODSHARD_RESONANCE: "Havoc gain increased range for Squad Sight, Target Lock, and Force Field.",
item_names.ZEALOT_SENTINEL_CENTURION_LEG_ENHANCEMENTS: "Zealots, Sentinels, and Centurions gain increased movement speed.",
item_names.ZEALOT_SENTINEL_CENTURION_SHIELD_CAPACITY: "Zealots, Sentinels, and Centurions gain +30 maximum shields.",
item_names.ZEALOT_WHIRLWIND: "Zealot War Council upgrade. Gives Zealots the whirlwind ability, dealing damage in an area over 3 seconds.",
item_names.CENTURION_RESOURCE_EFFICIENCY: _get_resource_efficiency_desc(item_names.CENTURION),
item_names.SENTINEL_RESOURCE_EFFICIENCY: _get_resource_efficiency_desc(item_names.SENTINEL),
item_names.STALKER_PHASE_REACTOR: "Stalkers restore 80 shields over 5 seconds after they Blink.",
item_names.SOA_CHRONO_SURGE: "The Spear of Adun increases a target structure's unit warp in and research speeds by +1000% for 20 seconds.",
item_names.SOA_PROGRESSIVE_PROXY_PYLON: inspect.cleandoc("""
Level 1: The Spear of Adun quickly warps in a Pylon to a target location.
Expand Down
5 changes: 5 additions & 0 deletions worlds/sc2/item_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class ItemGroupNames:
SOA_ITEMS = "SOA"
PROTOSS_GLOBAL_UPGRADES = "Protoss Global Upgrades"
PROTOSS_BUILDINGS = "Protoss Buildings"
WAR_COUNCIL = "Protoss War Council Upgrades"
AIUR_UNITS = "Aiur"
NERAZIM_UNITS = "Nerazim"
TAL_DARIM_UNITS = "Tal'Darim"
Expand Down Expand Up @@ -587,3 +588,7 @@ def get_all_group_names(cls) -> typing.Set[str]:
item_name_groups[ItemGroupNames.VANILLA_ITEMS] = vanilla_items = (
vanilla_wol_items + vanilla_hots_items + vanilla_lotv_items
)

item_name_groups[ItemGroupNames.WAR_COUNCIL] = [
item_name for item_name, item_data in items.item_table.items() if item_data.type == items.ProtossItemType.War_Council
]
6 changes: 6 additions & 0 deletions worlds/sc2/item_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,12 @@
ZEALOT_SENTINEL_CENTURION_LEG_ENHANCEMENTS = "Leg Enhancements (Zealot/Sentinel/Centurion)"
ZEALOT_SENTINEL_CENTURION_SHIELD_CAPACITY = "Shield Capacity (Zealot/Sentinel/Centurion)"

# War Council
ZEALOT_WHIRLWIND = "Whirlwind (Zealot)"
CENTURION_RESOURCE_EFFICIENCY = "Resource Efficiency (Centurion)"
SENTINEL_RESOURCE_EFFICIENCY = "Resource Efficiency (Sentinel)"
STALKER_PHASE_REACTOR = "Phase Reactor (Stalker)"

# Spear Of Adun
SOA_CHRONO_SURGE = "Chrono Surge (Spear of Adun Calldown)"
SOA_PROGRESSIVE_PROXY_PYLON = "Progressive Proxy Pylon (Spear of Adun Calldown)"
Expand Down
7 changes: 7 additions & 0 deletions worlds/sc2/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class ProtossItemType(ItemTypeEnum):
"""General Protoss unit upgrades"""
Forge_3 = "Forge", 9
"""General Protoss unit upgrades"""
War_Council = "War Council", 10


class FactionlessItemType(ItemTypeEnum):
Expand Down Expand Up @@ -1648,6 +1649,12 @@ def get_full_item_list():
item_names.ORACLE_BOSONIC_CORE: ItemData(378 + SC2LOTV_ITEM_ID_OFFSET, ProtossItemType.Forge_3, 18, SC2Race.PROTOSS, origin={"ext"}, parent_item=item_names.ORACLE),
item_names.SCOUT_RESOURCE_EFFICIENCY: ItemData(379 + SC2LOTV_ITEM_ID_OFFSET, ProtossItemType.Forge_3, 19, SC2Race.PROTOSS, origin={"ext"}, parent_item=item_names.SCOUT),

# War Council
item_names.ZEALOT_WHIRLWIND: ItemData(500 + SC2LOTV_ITEM_ID_OFFSET, ProtossItemType.War_Council, 0, SC2Race.PROTOSS, parent_item=item_names.ZEALOT),
item_names.CENTURION_RESOURCE_EFFICIENCY: ItemData(501 + SC2LOTV_ITEM_ID_OFFSET, ProtossItemType.War_Council, 1, SC2Race.PROTOSS, parent_item=item_names.CENTURION),
item_names.SENTINEL_RESOURCE_EFFICIENCY: ItemData(502 + SC2LOTV_ITEM_ID_OFFSET, ProtossItemType.War_Council, 2, SC2Race.PROTOSS, parent_item=item_names.SENTINEL),
item_names.STALKER_PHASE_REACTOR: ItemData(503 + SC2LOTV_ITEM_ID_OFFSET, ProtossItemType.War_Council, 3, SC2Race.PROTOSS, parent_item=item_names.STALKER),

# SoA Calldown powers
item_names.SOA_CHRONO_SURGE: ItemData(700 + SC2LOTV_ITEM_ID_OFFSET, ProtossItemType.Spear_Of_Adun, 0, SC2Race.PROTOSS, origin={"lotv"}),
item_names.SOA_PROGRESSIVE_PROXY_PYLON: ItemData(701 + SC2LOTV_ITEM_ID_OFFSET, ProtossItemType.Progressive, 0, SC2Race.PROTOSS, origin={"lotv"}, quantity=2),
Expand Down
9 changes: 9 additions & 0 deletions worlds/sc2/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,14 @@ class EnableMorphling(Toggle):
display_name = "Enable Morphling"


class AllowUnitNerfs(Toggle):
"""
Controls whether some units can initially be found in a nerfed state, with upgrades restoring their stronger power level.
For example, nerfed Zealots will lack the whirlwind upgrade until it is found as an item.
"""
display_name = "Allow Unit Nerfs"


class SpearOfAdunPresence(Choice):
"""
Determines in which missions Spear of Adun calldowns will be available.
Expand Down Expand Up @@ -929,6 +937,7 @@ class Starcraft2Options(PerGameCommonOptions):
start_primary_abilities: StartPrimaryAbilities
kerrigan_primal_status: KerriganPrimalStatus
enable_morphling: EnableMorphling
allow_unit_nerfs: AllowUnitNerfs
spear_of_adun_presence: SpearOfAdunPresence
spear_of_adun_present_in_no_build: SpearOfAdunPresentInNoBuild
spear_of_adun_autonomously_cast_ability_presence: SpearOfAdunAutonomouslyCastAbilityPresence
Expand Down
21 changes: 21 additions & 0 deletions worlds/sc2/test/test_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,3 +471,24 @@ def test_planetary_orbital_module_not_present_without_cc_spells(self) -> None:
self.assertTrue(itempool)
self.assertIn(item_names.PLANETARY_FORTRESS, itempool)
self.assertNotIn(item_names.PLANETARY_FORTRESS_ORBITAL_MODULE, itempool)

def test_disabling_unit_nerfs_removes_war_council_upgrades(self) -> None:
world_options = {
'enable_wol_missions': False,
'enable_prophecy_missions': True,
'enable_hots_missions': False,
'enable_lotv_prologue_missions': True,
'enable_lotv_missions': True,
'enable_epilogue_missions': False,
'enable_nco_missions': False,
'mission_order': options.MissionOrder.option_grid,
'allow_unit_nerfs': options.AllowUnitNerfs.option_false,
}

self.generate_world(world_options)
itempool = [item.name for item in self.multiworld.itempool]
war_council_item_names = set(item_groups.item_name_groups[item_groups.ItemGroupNames.WAR_COUNCIL])
present_war_council_items = war_council_item_names.intersection(itempool)

self.assertTrue(itempool)
self.assertFalse(present_war_council_items, f'Found war council upgrades when allow_unit_nerfs is false: {present_war_council_items}')

0 comments on commit f09220f

Please sign in to comment.