Skip to content

Commit

Permalink
Merge pull request #20 from Magnemania/sc2hots-wolports
Browse files Browse the repository at this point in the history
Port Short Hints and Generic Upgrade Options to WoL
  • Loading branch information
Salzkorn authored Apr 9, 2023
2 parents 7adde2d + 216afa0 commit b94c136
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 7 deletions.
38 changes: 33 additions & 5 deletions Starcraft2Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from sc2.main import run_game
from sc2.player import Bot
from worlds.sc2wol import SC2WoLWorld
from worlds.sc2wol.Items import lookup_id_to_name, item_table, ItemData, type_flaggroups
from worlds.sc2wol.Items import lookup_id_to_name, item_table, ItemData, type_flaggroups, upgrade_numbers
from worlds.sc2wol.Locations import SC2WOL_LOC_ID_OFFSET
from worlds.sc2wol.MissionTables import lookup_id_to_mission
from worlds.sc2wol.Regions import MissionInfo
Expand Down Expand Up @@ -192,6 +192,8 @@ class SC2Context(CommonContext):
announcements = queue.Queue()
sc2_run_task: typing.Optional[asyncio.Task] = None
missions_unlocked: bool = False # allow launching missions ignoring requirements
generic_upgrade_missions = 0
generic_upgrade_items = 0
current_tooltip = None
last_loc_list = None
difficulty_override = -1
Expand Down Expand Up @@ -223,6 +225,8 @@ def on_package(self, cmd: str, args: dict):
self.mission_order = args["slot_data"].get("mission_order", 0)
self.final_mission = args["slot_data"].get("final_mission", 29)
self.player_color = args["slot_data"].get("player_color", 2)
self.generic_upgrade_missions = args["slot_data"].get("generic_upgrade_missions", 0)
self.generic_upgrade_items = args["slot_data"].get("generic_upgrade_items", 0)

self.build_location_to_mission_mapping()

Expand Down Expand Up @@ -530,7 +534,8 @@ async def main():
]


def calculate_items(items: typing.List[NetworkItem]) -> typing.List[int]:
def calculate_items(ctx: SC2Context) -> typing.List[int]:
items = ctx.items_received
network_item: NetworkItem
accumulators: typing.List[int] = [0 for _ in type_flaggroups]

Expand All @@ -544,12 +549,35 @@ def calculate_items(items: typing.List[NetworkItem]) -> typing.List[int]:

# exists multiple times
elif item_data.type == "Upgrade":
accumulators[type_flaggroups[item_data.type]] += 1 << item_data.number
flaggroup = type_flaggroups[item_data.type]
if ctx.generic_upgrade_items == 0:
accumulators[flaggroup] += 1 << item_data.number
else:
for bundled_number in upgrade_numbers[item_data.number]:
accumulators[flaggroup] += 1 << bundled_number

# sum
else:
accumulators[type_flaggroups[item_data.type]] += item_data.number

# Upgrades from completed missions
if ctx.generic_upgrade_missions > 0:
upgrade_flaggroup = type_flaggroups["Upgrade"]
num_missions = ctx.generic_upgrade_missions * len(ctx.mission_req_table)
amounts = [
num_missions // 100,
2 * num_missions // 100,
3 * num_missions // 100
]
upgrade_count = 0
completed = len([id for id in ctx.mission_id_to_location_ids if SC2WOL_LOC_ID_OFFSET + victory_modulo * id in ctx.checked_locations])
for amount in amounts:
if completed >= amount:
upgrade_count += 1
# Equivalent to "Progressive Upgrade" item
for bundled_number in upgrade_numbers[5]:
accumulators[upgrade_flaggroup] += upgrade_count << bundled_number

return accumulators


Expand Down Expand Up @@ -602,7 +630,7 @@ async def on_step(self, iteration: int):
game_state = 0
if not self.setup_done:
self.setup_done = True
start_items = calculate_items(self.ctx.items_received)
start_items = calculate_items(self.ctx)
if self.ctx.difficulty_override >= 0:
difficulty = calc_difficulty(self.ctx.difficulty_override)
else:
Expand Down Expand Up @@ -632,7 +660,7 @@ async def on_step(self, iteration: int):
"Starcraft 2 (This is likely a map issue)")

if self.last_received_update < len(self.ctx.items_received):
current_items = calculate_items(self.ctx.items_received)
current_items = calculate_items(self.ctx)
await self.chat_send("UpdateTech {} {} {} {} {} {} {} {}".format(
current_items[0], current_items[1], current_items[2], current_items[3], current_items[4],
current_items[5], current_items[6], current_items[7]))
Expand Down
43 changes: 43 additions & 0 deletions worlds/sc2wol/Items.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ def get_full_item_list():
"Progressive Vehicle Armor": ItemData(104 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 6, quantity=3),
"Progressive Ship Weapon": ItemData(105 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 8, quantity=3),
"Progressive Ship Armor": ItemData(106 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 10, quantity=3),
# Upgrade bundle 'number' values are used as indices to get affected 'number's
"Progressive Weapon Upgrade": ItemData(107 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 0, quantity=3),
"Progressive Armor Upgrade": ItemData(108 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 1, quantity=3),
"Progressive Infantry Upgrade": ItemData(109 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 2, quantity=3),
"Progressive Vehicle Upgrade": ItemData(110 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 3, quantity=3),
"Progressive Starship Upgrade": ItemData(111 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 4, quantity=3),
"Progressive Upgrade": ItemData(112 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 5, quantity=3),

"Projectile Accelerator (Bunker)": ItemData(200 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 0, parent_item="Bunker"),
"Neosteel Bunker (Bunker)": ItemData(201 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 1, parent_item="Bunker"),
Expand Down Expand Up @@ -175,6 +182,9 @@ def get_basic_units(multiworld: MultiWorld, player: int) -> typing.Set[str]:
item_name_groups = {}
for item, data in item_table.items():
item_name_groups.setdefault(data.type, []).append(item)
if data.type in ("Armory 1", "Armory 2") and '(' in item:
short_name = item[:item.find(' (')]
item_name_groups[short_name] = [item]
item_name_groups["Missions"] = ["Beat " + mission_name for mission_name in vanilla_mission_req_table]

filler_items: typing.Tuple[str, ...] = (
Expand All @@ -200,6 +210,39 @@ def get_basic_units(multiworld: MultiWorld, player: int) -> typing.Set[str]:
"Psi Disruptor": 3
}

# 'number' values of upgrades for upgrade bundle items
upgrade_numbers = [
{0, 4, 8}, # Weapon
{2, 6, 10}, # Armor
{0, 2}, # Infantry
{4, 6}, # Vehicle
{8, 10}, # Starship
{0, 2, 4, 6, 8, 10} # All
]
# Names of upgrades to be included for different options
upgrade_included_names = [
{ # Individual Items
"Progressive Infantry Weapon",
"Progressive Infantry Armor",
"Progressive Vehicle Weapon",
"Progressive Vehicle Armor"
"Progressive Ship Weapon",
"Progressive Ship Armor"
},
{ # Bundle Weapon And Armor
"Progressive Weapon Upgrade",
"Progressive Armor Upgrade"
},
{ # Bundle Unit Class
"Progressive Infantry Upgrade",
"Progressive Vehicle Upgrade",
"Progressive Starship Upgrade"
},
{ # Bundle All
"Progressive Upgrade"
}
]

lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in get_full_item_list().items() if
data.code}
# Map type to expected int
Expand Down
34 changes: 34 additions & 0 deletions worlds/sc2wol/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,38 @@ class UnitsAlwaysHaveUpgrades(DefaultOnToggle):
display_name = "Units Always Have Upgrades"


class GenericUpgradeMissions(Range):
"""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."""
display_name = "Generic Upgrade Missions"
range_start = 0
range_end = 100
default = 0


class GenericUpgradeItems(Choice):
"""Determines how weapon and armor upgrades are split into items. All options produce 3 levels of each item.
Does nothing if upgrades are unlocked by completed mission counts.
Individual Items: All weapon and armor upgrades are each an item,
resulting in 18 total upgrade items.
Bundle Weapon And Armor: All types of weapon upgrades are one item,
and all types of armor upgrades are one item,
resulting in 6 total items.
Bundle Unit Class: Weapon and armor upgrades are merged,
but Infantry, Vehicle, and Starship upgrades are bundled separately,
resulting in 9 total items.
Bundle All: All weapon and armor upgrades are one item,
resulting in 3 total items."""
display_name = "Generic Upgrade Items"
option_individual_items = 0
option_bundle_weapon_and_armor = 1
option_bundle_unit_class = 2
option_bundle_all = 3


class LockedItems(ItemSet):
"""Guarantees that these items will be unlockable"""
display_name = "Locked Items"
Expand Down Expand Up @@ -150,6 +182,8 @@ class ExcludedMissions(OptionSet):
"early_unit": EarlyUnit,
"required_tactics": RequiredTactics,
"units_always_have_upgrades": UnitsAlwaysHaveUpgrades,
"generic_upgrade_missions": GenericUpgradeMissions,
"generic_upgrade_items": GenericUpgradeItems,
"locked_items": LockedItems,
"excluded_items": ExcludedItems,
"excluded_missions": ExcludedMissions
Expand Down
13 changes: 11 additions & 2 deletions worlds/sc2wol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
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
get_basic_units, ItemData, upgrade_included_names
from .Locations import get_locations
from .Regions import create_regions
from .Options import sc2wol_options, get_option_value
Expand Down Expand Up @@ -175,8 +175,17 @@ 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')

# 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')

def item_allowed(name: str, data: ItemData) -> bool:
return name not in excluded_items and \
(data.type != "Upgrade" or (include_upgrades and \
name in upgrade_included_names[upgrade_items]))

for name, data in item_table.items():
if name not in excluded_items:
if item_allowed(name, data):
for _ in range(data.quantity):
item = create_item_with_correct_settings(player, name)
if name in yaml_locked_items:
Expand Down

0 comments on commit b94c136

Please sign in to comment.