Skip to content

Commit

Permalink
Add an escape for W/A upgrades by mission beaten
Browse files Browse the repository at this point in the history
  • Loading branch information
Ziktofel committed Oct 25, 2024
1 parent f83fc70 commit 49149e9
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 15 deletions.
28 changes: 27 additions & 1 deletion worlds/sc2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
from math import floor, ceil
from dataclasses import dataclass
from BaseClasses import Item, MultiWorld, Location, Tutorial, ItemClassification, CollectionState
from Fill import fill_restrictive, FillError
from Options import Accessibility
from worlds.AutoWorld import WebWorld, World
from . import item_names
from .items import (
StarcraftItem, filler_items, get_full_item_list, ProtossItemType,
ItemData, kerrigan_actives, kerrigan_passives,
not_balanced_starting_units,
not_balanced_starting_units, WEAPON_ARMOR_UPGRADE_MAX_LEVEL,
)
from . import items
from . import item_groups
Expand All @@ -33,6 +34,8 @@
)
from .regions import create_mission_order
from .mission_order.structs import SC2MissionOrder
from ..stardew_valley import true_
from ..v6 import location_table

logger = logging.getLogger("Starcraft 2")

Expand Down Expand Up @@ -220,6 +223,29 @@ def fill_slot_data(self):
slot_data["kerrigan_presence"] = KerriganPresence.option_not_present
return slot_data

def pre_fill(self) -> None:
if self.options.generic_upgrade_missions > 0:
# Attempt to resolve situation when the option is too high for the mission order rolled
# TODO: Attempt to resolve Kerrigan levels too
weapon_armor_item_names = [
item_names.PROGRESSIVE_TERRAN_WEAPON_ARMOR_UPGRADE,
item_names.PROGRESSIVE_ZERG_WEAPON_ARMOR_UPGRADE,
item_names.PROGRESSIVE_PROTOSS_WEAPON_ARMOR_UPGRADE
]
for attempt in range(0, WEAPON_ARMOR_UPGRADE_MAX_LEVEL):
all_state: CollectionState = self.multiworld.get_all_state(False)
location_failed = False
for location in self.location_cache:
if not (all_state.can_reach_location(location.name, self.player)
and all_state.can_reach_region(location.parent_region.name, self.player)):
location_failed = True
break
if location_failed:
for item_name in weapon_armor_item_names:
item = self.multiworld.create_item(item_name, self.player)
self.multiworld.push_precollected(item)



def setup_events(player: int, locked_locations: List[str], location_cache: List[Location]):
for location in location_cache:
Expand Down
11 changes: 8 additions & 3 deletions worlds/sc2/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,13 +351,18 @@ class RequiredTactics(Choice):


class GenericUpgradeMissions(Range):
"""Determines the percentage of missions in the mission order that must be completed before
"""
Determines the percentage of missions in the mission order that must be completed before
level 1 of all weapon and armor upgrades is unlocked. Level 2 upgrades require double the amount of missions,
and level 3 requires triple the amount. The required amounts are always rounded down.
If set to 0, upgrades are instead added to the item pool and must be found to be used."""
If set to 0, upgrades are instead added to the item pool and must be found to be used.
If the mission order is unable to be beaten by this value (if above 0), the generator will place additional
weapon / armor upgrades into start inventory
"""
display_name = "Generic Upgrade Missions"
range_start = 0
range_end = 25 # Higher values lead to fails often
range_end = 100 # Higher values lead to fails often
default = 0


Expand Down
19 changes: 8 additions & 11 deletions worlds/sc2/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
from typing import TYPE_CHECKING, Set

from BaseClasses import CollectionState
from .item_groups import item_name_groups
from .item_names import PROGRESSIVE_TERRAN_SHIP_WEAPON, PROGRESSIVE_PROTOSS_WEAPON_UPGRADE, \
PROGRESSIVE_PROTOSS_GROUND_WEAPON, PROGRESSIVE_PROTOSS_GROUND_ARMOR, PROGRESSIVE_PROTOSS_AIR_WEAPON, \
PROGRESSIVE_PROTOSS_AIR_ARMOR, PROGRESSIVE_PROTOSS_SHIELDS
from .options import (
get_option_value, RequiredTactics, kerrigan_unit_available, AllInMap,
GrantStoryTech, GrantStoryLevels, TakeOverAIAllies, SpearOfAdunAutonomouslyCastAbilityPresence,
Expand Down Expand Up @@ -73,8 +69,9 @@ def weapon_armor_upgrade_count(self, upgrade_item: str, state: CollectionState)
assert upgrade_item in upgrade_bundle_inverted_lookup.keys()
count: int = 0
if self.generic_upgrade_missions > 0:
if not self.is_item_placement(state):
if (not self.is_item_placement(state)) or self.logic_level == RequiredTactics.option_no_logic:
# Item pool filtering, W/A upgrades aren't items
# No Logic: Don't care about W/A in this case
return WEAPON_ARMOR_UPGRADE_MAX_LEVEL
else:
count += floor(
Expand Down Expand Up @@ -166,7 +163,7 @@ def terran_air_anti_air(self, state: CollectionState) -> bool:
or (
self.advanced_tactics
and state.has_any({item_names.WRAITH, item_names.VALKYRIE, item_names.BATTLECRUISER}, self.player)
and self.weapon_armor_upgrade_count(PROGRESSIVE_TERRAN_SHIP_WEAPON, state) >= 2
and self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_TERRAN_SHIP_WEAPON, state) >= 2
)
)

Expand Down Expand Up @@ -1053,19 +1050,19 @@ def protoss_army_weapon_armor_upgrade_min_level(self, state: CollectionState) ->
if self.world_has_protoss_ground_unit():
count = min(
count,
self.weapon_armor_upgrade_count(PROGRESSIVE_PROTOSS_GROUND_WEAPON, state),
self.weapon_armor_upgrade_count(PROGRESSIVE_PROTOSS_GROUND_ARMOR, state)
self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_PROTOSS_GROUND_WEAPON, state),
self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_PROTOSS_GROUND_ARMOR, state)
)
if self.world_has_protoss_air_unit():
count = min(
count,
self.weapon_armor_upgrade_count(PROGRESSIVE_PROTOSS_AIR_WEAPON, state),
self.weapon_armor_upgrade_count(PROGRESSIVE_PROTOSS_AIR_ARMOR, state)
self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_PROTOSS_AIR_WEAPON, state),
self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_PROTOSS_AIR_ARMOR, state)
)
if self.world_has_protoss_ground_unit() or self.world_has_protoss_air_unit():
count = min(
count,
self.weapon_armor_upgrade_count(PROGRESSIVE_PROTOSS_SHIELDS, state),
self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_PROTOSS_SHIELDS, state),
)
return count

Expand Down
93 changes: 93 additions & 0 deletions worlds/sc2/test/test_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,3 +686,96 @@ def test_weapon_armor_upgrades_all_in_air(self):
# Under standard tactics you need to place L3 upgrades for available unit classes
self.assertGreaterEqual(len(vehicle_weapon_items), 3)
self.assertGreaterEqual(len(ship_weapon_items), 3)

def test_weapon_armor_upgrades_generic_upgrade_missions(self):
"""
Tests the case when there aren't enough missions in order to get required weapon/armor upgrades
for logic requirements.
:return:
"""
world_options = {
# Vanilla WoL with all missions
'mission_order': options.MissionOrder.option_vanilla,
'required_tactics': options.RequiredTactics.option_standard,
'starter_unit': options.StarterUnit.option_off,
'enable_prophecy_missions': False,
'enable_hots_missions': False,
'enable_lotv_prologue_missions': False,
'enable_lotv_missions': False,
'enable_epilogue_missions': False,
'enable_nco_missions': False,
'all_in_map': options.AllInMap.option_air, # All-in air forces an air unit
'start_inventory': {
item_names.GOLIATH: 1 # Don't fail with early item placement
},
'generic_upgrade_items': options.GenericUpgradeItems.option_individual_items,
'generic_upgrade_missions': 100, # Fallback happens by putting weapon/armor upgrades into starting inventory
}

self.generate_world(world_options)
starting_inventory = [item.name for item in self.multiworld.precollected_items[self.player]]
upgrade_items = [x for x in starting_inventory if x == item_names.PROGRESSIVE_TERRAN_WEAPON_ARMOR_UPGRADE]

# Under standard tactics you need to place L3 upgrades for available unit classes
self.assertEqual(len(upgrade_items), 3)

def test_weapon_armor_upgrades_generic_upgrade_missions_no_logic(self):
"""
Tests the case when there aren't enough missions in order to get required weapon/armor upgrades
for logic requirements.
Except the case above it's No Logic, thus the fallback won't take place.
:return:
"""
world_options = {
# Vanilla WoL with all missions
'mission_order': options.MissionOrder.option_vanilla,
'required_tactics': options.RequiredTactics.option_no_logic,
'starter_unit': options.StarterUnit.option_off,
'enable_prophecy_missions': False,
'enable_hots_missions': False,
'enable_lotv_prologue_missions': False,
'enable_lotv_missions': False,
'enable_epilogue_missions': False,
'enable_nco_missions': False,
'all_in_map': options.AllInMap.option_air, # All-in air forces an air unit
'start_inventory': {
item_names.GOLIATH: 1 # Don't fail with early item placement
},
'generic_upgrade_items': options.GenericUpgradeItems.option_individual_items,
'generic_upgrade_missions': 100, # Fallback happens by putting weapon/armor upgrades into starting inventory
}

self.generate_world(world_options)
starting_inventory = [item.name for item in self.multiworld.precollected_items[self.player]]
upgrade_items = [x for x in starting_inventory if x == item_names.PROGRESSIVE_TERRAN_WEAPON_ARMOR_UPGRADE]

# No logic won't take the fallback to trigger
self.assertEqual(len(upgrade_items), 0)

def test_weapon_armor_upgrades_generic_upgrade_missions_no_countermeasure_needed(self):
world_options = {
# Vanilla WoL with all missions
'mission_order': options.MissionOrder.option_vanilla,
'required_tactics': options.RequiredTactics.option_standard,
'starter_unit': options.StarterUnit.option_off,
'enable_prophecy_missions': False,
'enable_hots_missions': False,
'enable_lotv_prologue_missions': False,
'enable_lotv_missions': False,
'enable_epilogue_missions': False,
'enable_nco_missions': False,
'all_in_map': options.AllInMap.option_air, # All-in air forces an air unit
'start_inventory': {
item_names.GOLIATH: 1 # Don't fail with early item placement
},
'generic_upgrade_items': options.GenericUpgradeItems.option_individual_items,
'generic_upgrade_missions': 1, # Weapon / Armor upgrades should be available almost instantly
}

self.generate_world(world_options)
starting_inventory = [item.name for item in self.multiworld.precollected_items[self.player]]
upgrade_items = [x for x in starting_inventory if x == item_names.PROGRESSIVE_TERRAN_WEAPON_ARMOR_UPGRADE]

# No additional starting inventory item placement is needed
self.assertEqual(len(upgrade_items), 0)

0 comments on commit 49149e9

Please sign in to comment.