SC2: 0.4.3 bugfixes (#2273)
Co-authored-by: Matthew <matthew.marinets@gmail.com>
This commit is contained in:
parent
504d09daf6
commit
154e17f4ff
|
@ -9,6 +9,7 @@ import multiprocessing
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import typing
|
import typing
|
||||||
import queue
|
import queue
|
||||||
import zipfile
|
import zipfile
|
||||||
|
@ -286,6 +287,8 @@ class SC2Context(CommonContext):
|
||||||
await super(SC2Context, self).server_auth(password_requested)
|
await super(SC2Context, self).server_auth(password_requested)
|
||||||
await self.get_username()
|
await self.get_username()
|
||||||
await self.send_connect()
|
await self.send_connect()
|
||||||
|
if self.ui:
|
||||||
|
self.ui.first_check = True
|
||||||
|
|
||||||
def on_package(self, cmd: str, args: dict):
|
def on_package(self, cmd: str, args: dict):
|
||||||
if cmd in {"Connected"}:
|
if cmd in {"Connected"}:
|
||||||
|
@ -1166,10 +1169,12 @@ def download_latest_release_zip(owner: str, repo: str, api_version: str, metadat
|
||||||
|
|
||||||
r2 = requests.get(download_url, headers=headers)
|
r2 = requests.get(download_url, headers=headers)
|
||||||
if r2.status_code == 200 and zipfile.is_zipfile(io.BytesIO(r2.content)):
|
if r2.status_code == 200 and zipfile.is_zipfile(io.BytesIO(r2.content)):
|
||||||
with open(f"{repo}.zip", "wb") as fh:
|
tempdir = tempfile.gettempdir()
|
||||||
|
file = tempdir + os.sep + f"{repo}.zip"
|
||||||
|
with open(file, "wb") as fh:
|
||||||
fh.write(r2.content)
|
fh.write(r2.content)
|
||||||
sc2_logger.info(f"Successfully downloaded {repo}.zip.")
|
sc2_logger.info(f"Successfully downloaded {repo}.zip.")
|
||||||
return f"{repo}.zip", latest_metadata
|
return file, latest_metadata
|
||||||
else:
|
else:
|
||||||
sc2_logger.warning(f"Status code: {r2.status_code}")
|
sc2_logger.warning(f"Status code: {r2.status_code}")
|
||||||
sc2_logger.warning("Download failed.")
|
sc2_logger.warning("Download failed.")
|
||||||
|
|
|
@ -68,10 +68,10 @@ def get_locations(multiworld: Optional[MultiWorld], player: Optional[int]) -> Tu
|
||||||
lambda state: state._sc2wol_has_common_unit(multiworld, player) and
|
lambda state: state._sc2wol_has_common_unit(multiworld, player) and
|
||||||
(logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
|
(logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
|
||||||
or state._sc2wol_has_competent_anti_air(multiworld, player))),
|
or state._sc2wol_has_competent_anti_air(multiworld, player))),
|
||||||
LocationData("Evacuation", "Evacuation: First Chrysalis", SC2WOL_LOC_ID_OFFSET + 401, LocationType.BONUS),
|
LocationData("Evacuation", "Evacuation: North Chrysalis", SC2WOL_LOC_ID_OFFSET + 401, LocationType.BONUS),
|
||||||
LocationData("Evacuation", "Evacuation: Second Chrysalis", SC2WOL_LOC_ID_OFFSET + 402, LocationType.BONUS,
|
LocationData("Evacuation", "Evacuation: West Chrysalis", SC2WOL_LOC_ID_OFFSET + 402, LocationType.BONUS,
|
||||||
lambda state: state._sc2wol_has_common_unit(multiworld, player)),
|
lambda state: state._sc2wol_has_common_unit(multiworld, player)),
|
||||||
LocationData("Evacuation", "Evacuation: Third Chrysalis", SC2WOL_LOC_ID_OFFSET + 403, LocationType.BONUS,
|
LocationData("Evacuation", "Evacuation: East Chrysalis", SC2WOL_LOC_ID_OFFSET + 403, LocationType.BONUS,
|
||||||
lambda state: state._sc2wol_has_common_unit(multiworld, player)),
|
lambda state: state._sc2wol_has_common_unit(multiworld, player)),
|
||||||
LocationData("Evacuation", "Evacuation: Reach Hanson", SC2WOL_LOC_ID_OFFSET + 404, LocationType.MISSION_PROGRESS),
|
LocationData("Evacuation", "Evacuation: Reach Hanson", SC2WOL_LOC_ID_OFFSET + 404, LocationType.MISSION_PROGRESS),
|
||||||
LocationData("Evacuation", "Evacuation: Secret Resource Stash", SC2WOL_LOC_ID_OFFSET + 405, LocationType.BONUS),
|
LocationData("Evacuation", "Evacuation: Secret Resource Stash", SC2WOL_LOC_ID_OFFSET + 405, LocationType.BONUS),
|
||||||
|
@ -419,7 +419,7 @@ def get_locations(multiworld: Optional[MultiWorld], player: Optional[int]) -> Tu
|
||||||
lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
|
lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
|
||||||
LocationData("A Sinister Turn", "A Sinister Turn: Northeast Base", SC2WOL_LOC_ID_OFFSET + 2304, LocationType.MISSION_PROGRESS,
|
LocationData("A Sinister Turn", "A Sinister Turn: Northeast Base", SC2WOL_LOC_ID_OFFSET + 2304, LocationType.MISSION_PROGRESS,
|
||||||
lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
|
lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
|
||||||
LocationData("A Sinister Turn", "A Sinister Turn: Southeast Base", SC2WOL_LOC_ID_OFFSET + 2305, LocationType.MISSION_PROGRESS,
|
LocationData("A Sinister Turn", "A Sinister Turn: Southwest Base", SC2WOL_LOC_ID_OFFSET + 2305, LocationType.MISSION_PROGRESS,
|
||||||
lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
|
lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
|
||||||
LocationData("A Sinister Turn", "A Sinister Turn: Maar", SC2WOL_LOC_ID_OFFSET + 2306, LocationType.MISSION_PROGRESS,
|
LocationData("A Sinister Turn", "A Sinister Turn: Maar", SC2WOL_LOC_ID_OFFSET + 2306, LocationType.MISSION_PROGRESS,
|
||||||
lambda state: logic_level > 0 or state._sc2wol_has_protoss_medium_units(multiworld, player)),
|
lambda state: logic_level > 0 or state._sc2wol_has_protoss_medium_units(multiworld, player)),
|
||||||
|
|
|
@ -41,6 +41,10 @@ class FinalMap(Choice):
|
||||||
|
|
||||||
Vanilla mission order always ends with All in mission!
|
Vanilla mission order always ends with All in mission!
|
||||||
|
|
||||||
|
Warning: Using All-in with a short mission order (7 or fewer missions) is not recommended,
|
||||||
|
as there might not be enough locations to place all the required items,
|
||||||
|
any excess required items will be placed into the player's starting inventory!
|
||||||
|
|
||||||
This option is short-lived. It may be changed in the future
|
This option is short-lived. It may be changed in the future
|
||||||
"""
|
"""
|
||||||
display_name = "Final Map"
|
display_name = "Final Map"
|
||||||
|
@ -265,7 +269,6 @@ class MissionProgressLocations(LocationInclusion):
|
||||||
Nothing: No rewards for this type of tasks, effectively disabling such locations
|
Nothing: No rewards for this type of tasks, effectively disabling such locations
|
||||||
|
|
||||||
Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
|
Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
|
||||||
Warning: The generation may fail if too many locations are excluded by this way.
|
|
||||||
See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
|
See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
|
||||||
"""
|
"""
|
||||||
display_name = "Mission Progress Locations"
|
display_name = "Mission Progress Locations"
|
||||||
|
@ -282,7 +285,6 @@ class BonusLocations(LocationInclusion):
|
||||||
Nothing: No rewards for this type of tasks, effectively disabling such locations
|
Nothing: No rewards for this type of tasks, effectively disabling such locations
|
||||||
|
|
||||||
Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
|
Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
|
||||||
Warning: The generation may fail if too many locations are excluded by this way.
|
|
||||||
See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
|
See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
|
||||||
"""
|
"""
|
||||||
display_name = "Bonus Locations"
|
display_name = "Bonus Locations"
|
||||||
|
@ -300,7 +302,6 @@ class ChallengeLocations(LocationInclusion):
|
||||||
Nothing: No rewards for this type of tasks, effectively disabling such locations
|
Nothing: No rewards for this type of tasks, effectively disabling such locations
|
||||||
|
|
||||||
Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
|
Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
|
||||||
Warning: The generation may fail if too many locations are excluded by this way.
|
|
||||||
See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
|
See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
|
||||||
"""
|
"""
|
||||||
display_name = "Challenge Locations"
|
display_name = "Challenge Locations"
|
||||||
|
@ -317,7 +318,6 @@ class OptionalBossLocations(LocationInclusion):
|
||||||
Nothing: No rewards for this type of tasks, effectively disabling such locations
|
Nothing: No rewards for this type of tasks, effectively disabling such locations
|
||||||
|
|
||||||
Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
|
Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
|
||||||
Warning: The generation may fail if too many locations are excluded by this way.
|
|
||||||
See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
|
See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
|
||||||
"""
|
"""
|
||||||
display_name = "Optional Boss Locations"
|
display_name = "Optional Boss Locations"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from typing import Callable, Dict, List, Set
|
from typing import Callable, Dict, List, Set
|
||||||
from BaseClasses import MultiWorld, ItemClassification, Item, Location
|
from BaseClasses import MultiWorld, ItemClassification, Item, Location
|
||||||
from .Items import get_full_item_list, spider_mine_sources, second_pass_placeable_items, filler_items
|
from .Items import get_full_item_list, spider_mine_sources, second_pass_placeable_items, filler_items, \
|
||||||
|
progressive_if_nco
|
||||||
from .MissionTables import no_build_regions_list, easy_regions_list, medium_regions_list, hard_regions_list,\
|
from .MissionTables import no_build_regions_list, easy_regions_list, medium_regions_list, hard_regions_list,\
|
||||||
mission_orders, MissionInfo, alt_final_mission_locations, MissionPools
|
mission_orders, MissionInfo, alt_final_mission_locations, MissionPools
|
||||||
from .Options import get_option_value, MissionOrder, FinalMap, MissionProgressLocations, LocationInclusion
|
from .Options import get_option_value, MissionOrder, FinalMap, MissionProgressLocations, LocationInclusion
|
||||||
|
@ -15,7 +16,7 @@ UPGRADABLE_ITEMS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
BARRACKS_UNITS = {"Marine", "Medic", "Firebat", "Marauder", "Reaper", "Ghost", "Spectre"}
|
BARRACKS_UNITS = {"Marine", "Medic", "Firebat", "Marauder", "Reaper", "Ghost", "Spectre"}
|
||||||
FACTORY_UNITS = {"Hellion", "Vulture", "Goliath", "Diamondback", "Siege Tank", "Thor", "Predator", "Widow Mine"}
|
FACTORY_UNITS = {"Hellion", "Vulture", "Goliath", "Diamondback", "Siege Tank", "Thor", "Predator", "Widow Mine", "Cyclone"}
|
||||||
STARPORT_UNITS = {"Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", "Hercules", "Science Vessel", "Raven", "Liberator", "Valkyrie"}
|
STARPORT_UNITS = {"Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", "Hercules", "Science Vessel", "Raven", "Liberator", "Valkyrie"}
|
||||||
|
|
||||||
PROTOSS_REGIONS = {"A Sinister Turn", "Echoes of the Future", "In Utter Darkness"}
|
PROTOSS_REGIONS = {"A Sinister Turn", "Echoes of the Future", "In Utter Darkness"}
|
||||||
|
@ -93,7 +94,10 @@ def get_item_upgrades(inventory: List[Item], parent_item: Item or str):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_item_quantity(item):
|
def get_item_quantity(item: Item, multiworld: MultiWorld, player: int):
|
||||||
|
if (not get_option_value(multiworld, player, "nco_items")) \
|
||||||
|
and item.name in progressive_if_nco:
|
||||||
|
return 1
|
||||||
return get_full_item_list()[item.name].quantity
|
return get_full_item_list()[item.name].quantity
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,7 +142,7 @@ class ValidInventory:
|
||||||
if not all(requirement(self) for requirement in requirements):
|
if not all(requirement(self) for requirement in requirements):
|
||||||
# If item cannot be removed, lock or revert
|
# If item cannot be removed, lock or revert
|
||||||
self.logical_inventory.add(item.name)
|
self.logical_inventory.add(item.name)
|
||||||
for _ in range(get_item_quantity(item)):
|
for _ in range(get_item_quantity(item, self.multiworld, self.player)):
|
||||||
locked_items.append(copy_item(item))
|
locked_items.append(copy_item(item))
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
@ -197,15 +201,16 @@ class ValidInventory:
|
||||||
# Don't process general upgrades, they may have been pre-locked per-level
|
# Don't process general upgrades, they may have been pre-locked per-level
|
||||||
for item in items_to_lock:
|
for item in items_to_lock:
|
||||||
if item in inventory:
|
if item in inventory:
|
||||||
|
item_quantity = inventory.count(item)
|
||||||
# Unit upgrades, lock all levels
|
# Unit upgrades, lock all levels
|
||||||
for _ in range(inventory.count(item)):
|
for _ in range(item_quantity):
|
||||||
inventory.remove(item)
|
inventory.remove(item)
|
||||||
if item not in locked_items:
|
if item not in locked_items:
|
||||||
# Lock all the associated items if not already locked
|
# Lock all the associated items if not already locked
|
||||||
for _ in range(get_item_quantity(item)):
|
for _ in range(item_quantity):
|
||||||
locked_items.append(copy_item(item))
|
locked_items.append(copy_item(item))
|
||||||
if item in existing_items:
|
if item in existing_items:
|
||||||
existing_items.remove(item)
|
existing_items.remove(item)
|
||||||
|
|
||||||
if self.min_units_per_structure > 0 and self.has_units_per_structure():
|
if self.min_units_per_structure > 0 and self.has_units_per_structure():
|
||||||
requirements.append(lambda state: state.has_units_per_structure())
|
requirements.append(lambda state: state.has_units_per_structure())
|
||||||
|
@ -216,7 +221,13 @@ class ValidInventory:
|
||||||
|
|
||||||
while len(inventory) + len(locked_items) > inventory_size:
|
while len(inventory) + len(locked_items) > inventory_size:
|
||||||
if len(inventory) == 0:
|
if len(inventory) == 0:
|
||||||
raise Exception("Reduced item pool generation failed - not enough locations available to place items.")
|
# There are more items than locations and all of them are already locked due to YAML or logic.
|
||||||
|
# Random items from locked ones will go to starting items
|
||||||
|
self.multiworld.random.shuffle(locked_items)
|
||||||
|
while len(locked_items) > inventory_size:
|
||||||
|
item: Item = locked_items.pop()
|
||||||
|
self.multiworld.push_precollected(item)
|
||||||
|
break
|
||||||
# Select random item from removable items
|
# Select random item from removable items
|
||||||
item = self.multiworld.random.choice(inventory)
|
item = self.multiworld.random.choice(inventory)
|
||||||
# Cascade removals to associated items
|
# Cascade removals to associated items
|
||||||
|
@ -245,7 +256,7 @@ class ValidInventory:
|
||||||
for _ in range(inventory.count(transient_item)):
|
for _ in range(inventory.count(transient_item)):
|
||||||
inventory.remove(transient_item)
|
inventory.remove(transient_item)
|
||||||
if transient_item not in locked_items:
|
if transient_item not in locked_items:
|
||||||
for _ in range(get_item_quantity(transient_item)):
|
for _ in range(get_item_quantity(transient_item, self.multiworld, self.player)):
|
||||||
locked_items.append(copy_item(transient_item))
|
locked_items.append(copy_item(transient_item))
|
||||||
if transient_item.classification in (ItemClassification.progression, ItemClassification.progression_skip_balancing):
|
if transient_item.classification in (ItemClassification.progression, ItemClassification.progression_skip_balancing):
|
||||||
self.logical_inventory.add(transient_item.name)
|
self.logical_inventory.add(transient_item.name)
|
||||||
|
|
|
@ -11,6 +11,6 @@
|
||||||
markup: True
|
markup: True
|
||||||
halign: 'center'
|
halign: 'center'
|
||||||
valign: 'middle'
|
valign: 'middle'
|
||||||
padding_x: 5
|
padding: [5,0,5,0]
|
||||||
markup: True
|
markup: True
|
||||||
outline_width: 1
|
outline_width: 1
|
||||||
|
|
|
@ -34,7 +34,7 @@ class SC2WoLWorld(World):
|
||||||
|
|
||||||
game = "Starcraft 2 Wings of Liberty"
|
game = "Starcraft 2 Wings of Liberty"
|
||||||
web = Starcraft2WoLWebWorld()
|
web = Starcraft2WoLWebWorld()
|
||||||
data_version = 4
|
data_version = 5
|
||||||
|
|
||||||
item_name_to_id = {name: data.code for name, data in get_full_item_list().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)}
|
location_name_to_id = {location.name: location.code for location in get_locations(None, None)}
|
||||||
|
@ -46,7 +46,7 @@ class SC2WoLWorld(World):
|
||||||
mission_req_table = {}
|
mission_req_table = {}
|
||||||
final_mission_id: int
|
final_mission_id: int
|
||||||
victory_item: str
|
victory_item: str
|
||||||
required_client_version = 0, 3, 6
|
required_client_version = 0, 4, 3
|
||||||
|
|
||||||
def __init__(self, multiworld: MultiWorld, player: int):
|
def __init__(self, multiworld: MultiWorld, player: int):
|
||||||
super(SC2WoLWorld, self).__init__(multiworld, player)
|
super(SC2WoLWorld, self).__init__(multiworld, player)
|
||||||
|
|
Loading…
Reference in New Issue