Skip to content

Commit

Permalink
Add Early Unit & Plando tracking to client UI
Browse files Browse the repository at this point in the history
  • Loading branch information
Salzkorn committed Oct 9, 2023
1 parent 377d512 commit 418557e
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 17 deletions.
6 changes: 5 additions & 1 deletion worlds/sc2/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from Utils import init_logging, is_windows
from worlds.sc2.Options import MissionOrder, KerriganPrimalStatus, kerrigan_unit_available, Kerriganless, GameSpeed, \
GenericUpgradeItems, GenericUpgradeResearch, ColorChoice, GenericUpgradeMissions, KerriganCheckLevelPackSize, KerriganChecksPerLevelPack, \
LocationInclusion, MissionProgressLocations, OptionalBossLocations, ChallengeLocations, BonusLocations
LocationInclusion, MissionProgressLocations, OptionalBossLocations, ChallengeLocations, BonusLocations, EarlyUnit

if __name__ == "__main__":
init_logging("SC2Client", exception_logger="Client")
Expand Down Expand Up @@ -278,6 +278,8 @@ class SC2Context(CommonContext):
generic_upgrade_research = 0
generic_upgrade_items = 0
location_inclusions: typing.Dict[LocationType, LocationInclusion] = {}
plando_locations: typing.List[str] = []
early_unit = 1
current_tooltip = None
last_loc_list = None
difficulty_override = -1
Expand Down Expand Up @@ -340,6 +342,8 @@ def on_package(self, cmd: str, args: dict) -> None:
LocationType.CHALLENGE: args["slot_data"].get("challenge_locations", ChallengeLocations.default),
LocationType.OPTIONAL_BOSS: args["slot_data"].get("optional_boss_locations", OptionalBossLocations.default),
}
self.plando_locations = args["slot_data"].get("plando_locations", [])
self.early_unit = args["slot_data"].get("early_unit", EarlyUnit.default)

self.build_location_to_mission_mapping()

Expand Down
32 changes: 27 additions & 5 deletions worlds/sc2/ClientGui.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@

from CommonClient import CommonContext
from worlds.sc2.Client import SC2Context, calc_unfinished_missions, parse_unlock
from worlds.sc2.MissionTables import lookup_id_to_mission, lookup_name_to_mission, SC2Mission
from worlds.sc2.MissionTables import lookup_id_to_mission, lookup_name_to_mission, SC2Mission, starting_mission_locations
from worlds.sc2.Locations import LocationType, lookup_location_id_to_type
from worlds.sc2.Options import LocationInclusion
from worlds.sc2 import SC2World
from worlds.sc2 import SC2World, get_first_mission, get_early_unit_location_name


class HoverableButton(HoverBehavior, Button):
Expand Down Expand Up @@ -82,6 +82,7 @@ class SC2Manager(GameManager):
launching: Union[bool, int] = False # if int -> mission ID
refresh_from_launching = True
first_check = True
first_mission = ""
ctx: SC2Context

def __init__(self, ctx) -> None:
Expand Down Expand Up @@ -117,6 +118,7 @@ def build_mission_table(self, dt) -> None:
if self.ctx.mission_req_table:
self.last_checked_locations = self.ctx.checked_locations.copy()
self.first_check = False
self.first_mission = get_first_mission(self.ctx.mission_req_table)

self.mission_id_to_button = {}

Expand Down Expand Up @@ -159,7 +161,7 @@ def build_mission_table(self, dt) -> None:
mission_obj: SC2Mission = lookup_name_to_mission[mission]
mission_id: int = mission_obj.id
mission_data = self.ctx.mission_req_table[campaign][mission]
remaining_locations, remaining_count = self.sort_unfinished_locations(mission)
remaining_locations, plando_locations, early_unit, remaining_count = self.sort_unfinished_locations(mission)
# Map has uncollected locations
if mission in unfinished_missions:
if self.any_valuable_locations(remaining_locations):
Expand Down Expand Up @@ -202,6 +204,11 @@ def build_mission_table(self, dt) -> None:
else:
tooltip += f"\n{self.get_location_type_title(loctype)}:\n- "
tooltip += "\n- ".join(remaining_locations[loctype])
if early_unit:
tooltip += f"\nEarly Unit:\n- {early_unit}"
if len(plando_locations) > 0:
tooltip += f"\nPlando:\n- "
tooltip += "\n- ".join(plando_locations)

mission_button = MissionButton(text=text, size_hint_y=None, height=50)
mission_button.tooltip_text = tooltip
Expand Down Expand Up @@ -235,14 +242,29 @@ def mission_callback(self, button: MissionButton) -> None:
def finish_launching(self, dt):
self.launching = False

def sort_unfinished_locations(self, mission_name: str) -> (Dict[LocationType, List[str]], int):
def sort_unfinished_locations(self, mission_name: str) -> (Dict[LocationType, List[str]], List[str], str | None, int):
locations: Dict[LocationType, List[str]] = {loctype: [] for loctype in LocationType}
count = 0
for loc in self.ctx.locations_for_mission(mission_name):
if loc in self.ctx.missing_locations:
count += 1
locations[lookup_location_id_to_type[loc]].append(self.ctx.location_names[loc])
return locations, count

early_unit = None
if self.ctx.early_unit and mission_name == self.first_mission:
early_unit = get_early_unit_location_name(mission_name)
for loctype in LocationType:
if early_unit in locations[loctype]:
locations[loctype].remove(early_unit)

plando_locations = []
for plando_loc in self.ctx.plando_locations:
for loctype in LocationType:
if plando_loc in locations[loctype]:
locations[loctype].remove(plando_loc)
plando_locations.append(plando_loc)

return locations, plando_locations, early_unit, count

def any_valuable_locations(self, locations: Dict[LocationType, List[str]]) -> bool:
for loctype in LocationType:
Expand Down
30 changes: 19 additions & 11 deletions worlds/sc2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def fill_slot_data(self):
if not isinstance(slot_req_table[campaign.id][mission]["required_world"][index], dict):
slot_req_table[campaign.id][mission]["required_world"][index] = slot_req_table[campaign.id][mission]["required_world"][index]._asdict()

slot_data["plando_locations"] = get_plando_locations(self.multiworld, self.player)
slot_data["mission_req"] = slot_req_table
slot_data["final_mission"] = self.final_mission_id
slot_data["version"] = 3
Expand Down Expand Up @@ -212,11 +213,7 @@ def assign_starter_items(multiworld: MultiWorld, player: int, excluded_items: Se
starter_items: List[Item] = []
non_local_items = multiworld.non_local_items[player].value
if get_option_value(multiworld, player, "early_unit"):
# The first world should also be the starting world
campaigns = multiworld.worlds[player].mission_req_table.keys()
lowest_id = min([campaign.id for campaign in campaigns])
first_campaign = [campaign for campaign in campaigns if campaign.id == lowest_id][0]
first_mission = list(multiworld.worlds[player].mission_req_table[first_campaign])[0]
first_mission = get_first_mission(multiworld.worlds[player].mission_req_table)
first_race = lookup_name_to_mission[first_mission].race

local_basic_unit = sorted(item for item in get_basic_units(multiworld, player, first_race) if item not in non_local_items and item not in excluded_items)
Expand All @@ -225,12 +222,7 @@ def assign_starter_items(multiworld: MultiWorld, player: int, excluded_items: Se
if not local_basic_unit:
raise Exception("Early Unit: At least one basic unit must be included")

if first_mission in starting_mission_locations:
first_location = starting_mission_locations[first_mission]
elif first_mission == "In Utter Darkness":
first_location = first_mission + ": Defeat"
else:
first_location = first_mission + ": Victory"
first_location = get_early_unit_location_name(first_mission)

starter_items.append(assign_starter_item(multiworld, player, excluded_items, locked_locations, first_location, local_basic_unit))

Expand Down Expand Up @@ -258,6 +250,22 @@ def assign_starter_items(multiworld: MultiWorld, player: int, excluded_items: Se

return starter_items

def get_first_mission(mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]]) -> str:
# The first world should also be the starting world
campaigns = mission_req_table.keys()
lowest_id = min([campaign.id for campaign in campaigns])
first_campaign = [campaign for campaign in campaigns if campaign.id == lowest_id][0]
first_mission = list(mission_req_table[first_campaign])[0]
return first_mission

def get_early_unit_location_name(first_mission: str) -> str:
if first_mission in starting_mission_locations:
first_location = starting_mission_locations[first_mission]
elif first_mission == "In Utter Darkness":
first_location = first_mission + ": Defeat"
else:
first_location = first_mission + ": Victory"
return first_location

def assign_starter_item(multiworld: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str],
location: str, item_list: Sequence[str]) -> Item:
Expand Down

0 comments on commit 418557e

Please sign in to comment.