mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
SC2 WoL - Mod, Item and Location update (#2113)
Migrates SC2 WoL world to the new mod with new items and locations. The new mod has a different architecture making it more future proof (with planned adding of other campaigns). Also gets rid of several old bugs Adds new short game formats intended for sync games (Tiny Grid, Mini Gauntlet). The final mission isn't decided by campaign length anymore but it's configurable instead. Allow excluding missions for Vanilla Shuffled, corrected some documentation. NOTE: This is a squashed commit with Salz' HotS excluded (not ready for the release and I plan multi-campaign instead) --------- Co-authored-by: Matthew <matthew.marinets@gmail.com>
This commit is contained in:
@@ -3,11 +3,11 @@ import typing
|
||||
from typing import List, Set, Tuple, Dict
|
||||
from BaseClasses import Item, MultiWorld, Location, Tutorial, ItemClassification
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from .Items import StarcraftWoLItem, item_table, filler_items, item_name_groups, get_full_item_list, \
|
||||
get_basic_units
|
||||
from .Locations import get_locations
|
||||
from .Items import StarcraftWoLItem, filler_items, item_name_groups, get_item_table, get_full_item_list, \
|
||||
get_basic_units, ItemData, upgrade_included_names, progressive_if_nco
|
||||
from .Locations import get_locations, LocationType
|
||||
from .Regions import create_regions
|
||||
from .Options import sc2wol_options, get_option_value
|
||||
from .Options import sc2wol_options, get_option_value, LocationInclusion
|
||||
from .LogicMixin import SC2WoLLogic
|
||||
from .PoolFilter import filter_missions, filter_items, get_item_upgrades
|
||||
from .MissionTables import starting_mission_locations, MissionInfo
|
||||
@@ -36,7 +36,7 @@ class SC2WoLWorld(World):
|
||||
web = Starcraft2WoLWebWorld()
|
||||
data_version = 4
|
||||
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
item_name_to_id = {name: data.code for name, data in get_full_item_list().items()}
|
||||
location_name_to_id = {location.name: location.code for location in get_locations(None, None)}
|
||||
option_definitions = sc2wol_options
|
||||
|
||||
@@ -69,6 +69,8 @@ class SC2WoLWorld(World):
|
||||
|
||||
starter_items = assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations)
|
||||
|
||||
filter_locations(self.multiworld, self.player, self.locked_locations, self.location_cache)
|
||||
|
||||
pool = get_item_pool(self.multiworld, self.player, self.mission_req_table, starter_items, excluded_items, self.location_cache)
|
||||
|
||||
fill_item_pool_with_dummy_items(self, self.multiworld, self.player, self.locked_locations, self.location_cache, pool)
|
||||
@@ -109,16 +111,6 @@ def setup_events(player: int, locked_locations: typing.List[str], location_cache
|
||||
def get_excluded_items(multiworld: MultiWorld, player: int) -> Set[str]:
|
||||
excluded_items: Set[str] = set()
|
||||
|
||||
if get_option_value(multiworld, player, "upgrade_bonus") == 1:
|
||||
excluded_items.add("Ultra-Capacitors")
|
||||
else:
|
||||
excluded_items.add("Vanadium Plating")
|
||||
|
||||
if get_option_value(multiworld, player, "bunker_upgrade") == 1:
|
||||
excluded_items.add("Shrike Turret")
|
||||
else:
|
||||
excluded_items.add("Fortified Bunker")
|
||||
|
||||
for item in multiworld.precollected_items[player]:
|
||||
excluded_items.add(item.name)
|
||||
|
||||
@@ -167,7 +159,7 @@ def assign_starter_item(multiworld: MultiWorld, player: int, excluded_items: Set
|
||||
|
||||
|
||||
def get_item_pool(multiworld: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo],
|
||||
starter_items: List[str], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]:
|
||||
starter_items: List[Item], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]:
|
||||
pool: List[Item] = []
|
||||
|
||||
# For the future: goal items like Artifact Shards go here
|
||||
@@ -176,17 +168,43 @@ def get_item_pool(multiworld: MultiWorld, player: int, mission_req_table: Dict[s
|
||||
# YAML items
|
||||
yaml_locked_items = get_option_value(multiworld, player, 'locked_items')
|
||||
|
||||
for name, data in item_table.items():
|
||||
if name not in excluded_items:
|
||||
for _ in range(data.quantity):
|
||||
item = create_item_with_correct_settings(player, name)
|
||||
if name in yaml_locked_items:
|
||||
locked_items.append(item)
|
||||
else:
|
||||
pool.append(item)
|
||||
# Adjust generic upgrade availability based on options
|
||||
include_upgrades = get_option_value(multiworld, player, 'generic_upgrade_missions') == 0
|
||||
upgrade_items = get_option_value(multiworld, player, 'generic_upgrade_items')
|
||||
|
||||
# Include items from outside Wings of Liberty
|
||||
item_sets = {'wol'}
|
||||
if get_option_value(multiworld, player, 'nco_items'):
|
||||
item_sets.add('nco')
|
||||
if get_option_value(multiworld, player, 'bw_items'):
|
||||
item_sets.add('bw')
|
||||
if get_option_value(multiworld, player, 'ext_items'):
|
||||
item_sets.add('ext')
|
||||
|
||||
def allowed_quantity(name: str, data: ItemData) -> int:
|
||||
if name in excluded_items \
|
||||
or data.type == "Upgrade" and (not include_upgrades or name not in upgrade_included_names[upgrade_items]) \
|
||||
or not data.origin.intersection(item_sets):
|
||||
return 0
|
||||
elif name in progressive_if_nco and 'nco' not in item_sets:
|
||||
return 1
|
||||
else:
|
||||
return data.quantity
|
||||
|
||||
for name, data in get_item_table(multiworld, player).items():
|
||||
for i in range(allowed_quantity(name, data)):
|
||||
item = create_item_with_correct_settings(player, name)
|
||||
if name in yaml_locked_items:
|
||||
locked_items.append(item)
|
||||
else:
|
||||
pool.append(item)
|
||||
|
||||
existing_items = starter_items + [item for item in multiworld.precollected_items[player]]
|
||||
existing_names = [item.name for item in existing_items]
|
||||
|
||||
# Check the parent item integrity, exclude items
|
||||
pool[:] = [item for item in pool if pool_contains_parent(item, pool + locked_items + existing_items)]
|
||||
|
||||
# Removing upgrades for excluded items
|
||||
for item_name in excluded_items:
|
||||
if item_name in existing_names:
|
||||
@@ -207,8 +225,100 @@ def fill_item_pool_with_dummy_items(self: SC2WoLWorld, multiworld: MultiWorld, p
|
||||
|
||||
|
||||
def create_item_with_correct_settings(player: int, name: str) -> Item:
|
||||
data = item_table[name]
|
||||
data = get_full_item_list()[name]
|
||||
|
||||
item = Item(name, data.classification, data.code, player)
|
||||
|
||||
return item
|
||||
|
||||
|
||||
def pool_contains_parent(item: Item, pool: [Item]):
|
||||
item_data = get_full_item_list().get(item.name)
|
||||
if item_data.parent_item is None:
|
||||
# The item has not associated parent, the item is valid
|
||||
return True
|
||||
parent_item = item_data.parent_item
|
||||
# Check if the pool contains the parent item
|
||||
return parent_item in [pool_item.name for pool_item in pool]
|
||||
|
||||
|
||||
def filter_locations(multiworld: MultiWorld, player, locked_locations: List[str], location_cache: List[Location]):
|
||||
"""
|
||||
Filters the locations in the world using a trash or Nothing item
|
||||
:param multiworld:
|
||||
:param player:
|
||||
:param locked_locations:
|
||||
:param location_cache:
|
||||
:return:
|
||||
"""
|
||||
open_locations = [location for location in location_cache if location.item is None]
|
||||
plando_locations = get_plando_locations(multiworld, player)
|
||||
mission_progress_locations = get_option_value(multiworld, player, "mission_progress_locations")
|
||||
bonus_locations = get_option_value(multiworld, player, "bonus_locations")
|
||||
challenge_locations = get_option_value(multiworld, player, "challenge_locations")
|
||||
optional_boss_locations = get_option_value(multiworld, player, "optional_boss_locations")
|
||||
location_data = get_locations(multiworld, player)
|
||||
for location in open_locations:
|
||||
# Go through the locations that aren't locked yet (early unit, etc)
|
||||
if location.name not in plando_locations:
|
||||
# The location is not plando'd
|
||||
sc2_location = [sc2_location for sc2_location in location_data if sc2_location.name == location.name][0]
|
||||
location_type = sc2_location.type
|
||||
|
||||
if location_type == LocationType.MISSION_PROGRESS \
|
||||
and mission_progress_locations != LocationInclusion.option_enabled:
|
||||
item_name = get_exclusion_item(multiworld, mission_progress_locations)
|
||||
place_exclusion_item(item_name, location, locked_locations, player)
|
||||
|
||||
if location_type == LocationType.BONUS \
|
||||
and bonus_locations != LocationInclusion.option_enabled:
|
||||
item_name = get_exclusion_item(multiworld, bonus_locations)
|
||||
place_exclusion_item(item_name, location, locked_locations, player)
|
||||
|
||||
if location_type == LocationType.CHALLENGE \
|
||||
and challenge_locations != LocationInclusion.option_enabled:
|
||||
item_name = get_exclusion_item(multiworld, challenge_locations)
|
||||
place_exclusion_item(item_name, location, locked_locations, player)
|
||||
|
||||
if location_type == LocationType.OPTIONAL_BOSS \
|
||||
and optional_boss_locations != LocationInclusion.option_enabled:
|
||||
item_name = get_exclusion_item(multiworld, optional_boss_locations)
|
||||
place_exclusion_item(item_name, location, locked_locations, player)
|
||||
|
||||
|
||||
def place_exclusion_item(item_name, location, locked_locations, player):
|
||||
item = create_item_with_correct_settings(player, item_name)
|
||||
location.place_locked_item(item)
|
||||
locked_locations.append(location.name)
|
||||
|
||||
|
||||
def get_exclusion_item(multiworld: MultiWorld, option) -> str:
|
||||
"""
|
||||
Gets the exclusion item according to settings (trash/nothing)
|
||||
:param multiworld:
|
||||
:param option:
|
||||
:return: Item used for location exclusion
|
||||
"""
|
||||
if option == LocationInclusion.option_nothing:
|
||||
return "Nothing"
|
||||
elif option == LocationInclusion.option_trash:
|
||||
index = multiworld.random.randint(0, len(filler_items) - 1)
|
||||
return filler_items[index]
|
||||
raise Exception(f"Unsupported option type: {option}")
|
||||
|
||||
|
||||
def get_plando_locations(multiworld: MultiWorld, player) -> List[str]:
|
||||
"""
|
||||
|
||||
:param multiworld:
|
||||
:param player:
|
||||
:return: A list of locations affected by a plando in a world
|
||||
"""
|
||||
plando_locations = []
|
||||
for plando_setting in multiworld.plando_items[player]:
|
||||
plando_locations += plando_setting.get("locations", [])
|
||||
plando_setting_location = plando_setting.get("location", None)
|
||||
if plando_setting_location is not None:
|
||||
plando_locations.append(plando_setting_location)
|
||||
|
||||
return plando_locations
|
||||
|
||||
Reference in New Issue
Block a user