diff --git a/worlds/lol/Client.py b/worlds/lol/Client.py index 3234844ce4d1..a809100122c1 100644 --- a/worlds/lol/Client.py +++ b/worlds/lol/Client.py @@ -22,103 +22,6 @@ os.makedirs(game_communication_path) -###API FUNCTIONS### -def get_header(api_key): - return { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", - "Accept-Language": "en-US,en;q=0.9", - "Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8", - "Origin": "https://developer.riotgames.com", - "X-Riot-Token": api_key - } - -def get_puuid_by_riot_id(game_name, tag_line, api_key, region_long): - url = "https://" + region_long + ".api.riotgames.com/riot/account/v1/accounts/by-riot-id/" + game_name + "/" + tag_line - response = requests.get(url, headers=get_header(api_key)) - if str(response) == "": - return "Forbidden, check your API Key" - elif str(response) == "": - return "Data not found. Check your Riot ID, Tag Line, or configure your region." - else: - return json.loads(response.text)["puuid"] - -def get_last_match_id_by_puuid(puuid, api_key, region_long): - url = "https://" + region_long + ".api.riotgames.com/lol/match/v5/matches/by-puuid/" + str(puuid) + "/ids?start=0&count=1" - response = requests.get(url, headers=get_header(api_key)) - return json.loads(response.text)[0] - -def get_match_info_by_match_id(match_id, api_key, region_long): - url = "https://" + region_long + ".api.riotgames.com/lol/match/v5/matches/" + str(match_id) - response = requests.get(url, headers=get_header(api_key)) - return json.loads(response.text) - -def get_item_ids_purchased(puuid, match_info): - item_ids = [] - item_slots = ["item0", "item1", "item2", "item3", "item4", "item5", "item6"] - for participant in match_info["info"]["participants"]: - if participant["puuid"] == puuid: - for item_slot in item_slots: - if item_slot in participant.keys(): - item_ids.append(participant[item_slot]) - return item_ids - -def won_match(puuid, match_info): - for participant in match_info["info"]["participants"]: - if participant["puuid"] == puuid: - return participant["win"] - return False - -def get_collected_item_ids(): - item_ids = [] - for root, dirs, files in os.walk(game_communication_path): - for file in files: - if str(file).startswith("AP"): - with open(os.path.join(game_communication_path, file), 'r') as f: - item_id = int(f.readline()) - item_ids.append(item_id) - f.close() - return item_ids - -def get_sent_location_ids(): - sent_location_ids = [] - for root, dirs, files in os.walk(game_communication_path): - for file in files: - if str(file).startswith("send"): - sent_location_ids.append(str(file).replace("send", "")) - return sent_location_ids - -def get_available_checks(): - item_ids = [int(str(item_id)[4:]) for item_id in get_collected_item_ids()] - sent_location_ids = [int(str(location_id)[4:]) for location_id in get_sent_location_ids()] - available_checks = list(set(item_ids)-set(sent_location_ids)) - return available_checks - -def get_game_mode_offset(game_mode): - if game_mode == "ARAM": - return 5652000000 - elif game_mode == "CHERRY": - return 5653000000 - else: - return 5651000000 - -def get_item_name(check_item_id): - versions_url = "https://ddragon.leagueoflegends.com/api/versions.json" - most_recent_version = requests.get(versions_url).json()[0] - items_url = "https://ddragon.leagueoflegends.com/cdn/" + str(most_recent_version) + "/data/en_US/item.json" - item_data = requests.get(items_url).json()["data"] - for item_id in list(item_data.keys()): - if int(check_item_id) == int(item_id): - return item_data[item_id]["name"] - return "Not Found" - -def send_check(item_id): - with open(os.path.join(game_communication_path, "send" + str(item_id)), 'w') as f: - f.close() - -def send_victory(): - with open(os.path.join(game_communication_path, "victory"), 'w') as f: - f.close() - ###Client### if __name__ == "__main__": Utils.init_logging("LOLClient", exception_logger="Client") @@ -133,133 +36,7 @@ def check_stdin() -> None: print("WARNING: Console input is not routed reliably on Windows, use the GUI instead.") class LOLClientCommandProcessor(ClientCommandProcessor): - api_key = "" - player_puuid = "" - region_short = "na1" - region_long = "americas" - - region_short_options = ["br1" , "la1" , "la2" , "na1" , "jp1" , "kr" , "tw2" , "eun1" , "euw1" , "ru" , "tr1" , "oc1", "ph2", "sg" , "th2", "vn2"] - region_long_options = ["americas", "americas", "americas", "americas", "asia", "asia", "asia", "europe", "europe", "europe", "europe", "sea", "sea", "sea", "sea", "sea"] - - def _cmd_set_api_key(self, api_key): - """Set the API Key for RIOT API""" - self.api_key = api_key - self.output(f"API Key Set") - - def _cmd_set_riot_id(self, riot_id_and_tag_line): - """Set the PUUID from Riot API using the passed Riot ID and Tag Line""" - if "#" not in riot_id_and_tag_line: - self.output(f"Please format your input like: RIOT_ID#TAG_LINE") - else: - riot_id = riot_id_and_tag_line.split("#")[0] - tag_line = riot_id_and_tag_line.split("#")[1] - if self.api_key != "": - self.player_puuid = get_puuid_by_riot_id(riot_id, tag_line, self.api_key, self.region_long) - if self.player_puuid == "Forbidden, check your API Key": - self.output("Forbidden, check your API Key") - self.player_puuid = "" - elif self.player_puuid == "Data not found. Check your Riot ID, Tag Line, or configure your region.": - self.output("Data not found. Check your Riot ID, Tag Line, or configure your region.") - self.player_puuid = "" - else: - self.output(f"PUUID Set") - else: - self.output(f"Please set your API Key") - - def _cmd_set_region(self, region_number): - """Sets the region number. Default is NA""" - if region_number.isnumeric(): - region_number = int(region_number) - if region_number >= 0 and region_number < len(self.region_short_options): - self.region_short = self.region_short_options[region_number] - self.region_long = self.region_long_options[region_number] - self.output(f"Region set: " + self.region_short + " - " + self.region_long) - else: - self.output(f"Invalid int. Please choose a valid option. View options by running /print_region_options") - else: - self.output(f"Invalid integer passed. Please pass a valid option. View options by running /print_region_options") - - def _cmd_check_last_match(self): - """Checks the last match for victory with unlocked items""" - new_locations = [] - if self.api_key != "" and self.player_puuid != "": - unlocked_item_ids = get_collected_item_ids() - if len(unlocked_item_ids) > 0: - last_match_id = get_last_match_id_by_puuid(self.player_puuid, self.api_key, self.region_long) - last_match_info = get_match_info_by_match_id(last_match_id, self.api_key, self.region_long) - game_mode_offset = get_game_mode_offset(last_match_info["info"]["gameMode"]) - if won_match(self.player_puuid, last_match_info): - item_ids_purchased = get_item_ids_purchased(self.player_puuid, last_match_info) - self.output("Item IDs Unlocked: " + str(unlocked_item_ids)) - for item_id in item_ids_purchased: - self.output("Item ID Purchased: " + str(int(item_id) + game_mode_offset + 10000000)) - if int(item_id) + game_mode_offset in unlocked_item_ids: - new_locations.append(int(item_id) + game_mode_offset + 10000000) - else: - self.output(f"Last Match Resulted in a Loss...") - else: - self.output(f"You have no items!") - else: - self.output(f"Please set your API Key and Summoner Name") - if len(new_locations) > 0: - for location in new_locations: - send_check(location) - else: - self.output(f"No new valid items") - - def _cmd_receive_starting_items(self): - """When you're ready to start your run, this receives your starting items""" - starting_location_ids = [5660_000001, 5660_000002, 5660_000003, 5660_000004, 5660_000005, 5660_000006] - for location_id in starting_location_ids: - with open(os.path.join(game_communication_path, "send" + str(location_id)), 'w') as f: - f.close() - self.output("Items Received") - - def _cmd_check_for_victory(self): - victory_item_ids = [5650000001, 5650000002, 5650000003, 5650000004, 5650000005, 5650000006] - victory_items_collected = 0 - item_ids = get_collected_item_ids() - for item_id in item_ids: - if int(item_id) in victory_item_ids: - victory_items_collected = victory_items_collected + 1 - if victory_items_collected >= len(victory_item_ids): - send_victory() - else: - self.output("You have " + str(victory_items_collected) + " out of " + str(len(victory_item_ids)) + " victory items collected.") - - def _cmd_print_item_ids(self): - """Prints currently collected item ids""" - item_ids = get_collected_item_ids() - for item_id in item_ids: - self.output(item_id) - - def _cmd_print_puuid(self): - """Prints the defined PUUID""" - self.output(self.player_puuid) - - def _cmd_print_api_key(self): - """Prints the defined API Key""" - self.output(self.api_key) - - def _cmd_print_region(self): - """Prints currently selected region.""" - self.output(f"Region: " + self.region_short + " - " + self.region_long) - - def _cmd_print_region_options(self): - """Prints all region options""" - i = 0 - while i < len(self.region_short_options): - self.output(f"Region " + str(i) + ": " + self.region_short_options[i] + " - " + self.region_long_options[i]) - i = i + 1 - - def _cmd_print_available_checks(self): - """Prints all available items for which you can receive checks.""" - available_checks = get_available_checks() - if len(available_checks) > 0: - for available_check in available_checks: - self.output(get_item_name(available_check)) - else: - self.output(f"No available items!") + pass class LOLContext(CommonContext): command_processor: int = LOLClientCommandProcessor @@ -318,6 +95,13 @@ def on_package(self, cmd: str, args: dict): filename = f"send{ss}" with open(os.path.join(self.game_communication_path, filename), 'w') as f: f.close() + #Handle Slot Data + for slot_data_key in list(args['slot_data'].keys()): + with open(os.path.join(self.game_communication_path, slot_data_key.replace(" ", "_") + ".cfg"), 'w') as f: + f.write(str(args['slot_data'][slot_data_key])) + f.close() + #End Handle Slot Data + if cmd in {"ReceivedItems"}: start_index = args["index"] if start_index != len(self.items_received): @@ -337,7 +121,7 @@ def on_package(self, cmd: str, args: dict): item_id = str(f.readline()).replace("\n", "") location_id = str(f.readline()).replace("\n", "") player = str(f.readline()).replace("\n", "") - if str(item_id) == str(NetworkItem(*item).item) and str(location_id) == str(NetworkItem(*item).location) and str(player) == str(NetworkItem(*item).player): + if str(item_id) == str(NetworkItem(*item).item) and str(location_id) == str(NetworkItem(*item).location) and str(player) == str(NetworkItem(*item).player) and int(location_id) > 0: found = True if not found: filename = f"AP_{str(check_num+1)}.item" diff --git a/worlds/lol/Connector.py b/worlds/lol/Connector.py new file mode 100644 index 000000000000..cf8dc80b9085 --- /dev/null +++ b/worlds/lol/Connector.py @@ -0,0 +1,286 @@ +import PySimpleGUI as sg +import json +import requests +import os + +###GET CHAMPION DATA### +versions_url = "https://ddragon.leagueoflegends.com/api/versions.json" +most_recent_version = requests.get(versions_url).json()[0] +champions_url = "https://ddragon.leagueoflegends.com/cdn/" + str(most_recent_version) + "/data/en_US/champion.json" +champions = {} +champion_data = requests.get(champions_url).json()["data"] + +for champion in list(champion_data.keys()): + champions[int(champion_data[champion]["key"])] = champion_data[champion] + +###SET GLOBAL VARIABLES### +url = "https://127.0.0.1:2999/liveclientdata/allgamedata" +unlocked_champion_ids = [] +total_lp_gained = 0 +in_match = False +game_values = { + "required_assists": 0, + "required_cs" : 0, + "required_kills" : 0, + "required_lp" : 0, + "required_vs" : 0, + "current_lp" : 0 +} + +###SET UP GAME COMMUNICATION PATH### +if "localappdata" in os.environ: + game_communication_path = os.path.expandvars(r"%localappdata%/LOLAP") +else: + game_communication_path = os.path.expandvars(r"$HOME/LOLAP") +if not os.path.exists(game_communication_path): + os.makedirs(game_communication_path) + + +###DEFINE FUNCTIONS### +def get_game_data(): + try: + return requests.get(url, verify=False).json() + except: + return None + +def get_items(game_values): + game_values["current_lp"] = 0 + unlocked_champion_ids.clear() + for root, dirs, files in os.walk(game_communication_path): + for file in files: + if file.startswith("AP"): + with open(os.path.join(game_communication_path, file), 'r') as f: + item_id = int(f.readline()) + if item_id % 565000000 == 0: + game_values["current_lp"] = game_values["current_lp"] + 1 + else: + unlocked_champion_ids.append(item_id % 565000000) + f.close() + +def read_cfg(game_values): + for root, dirs, files in os.walk(game_communication_path): + if "Required_Assists.cfg" in files: + with open(os.path.join(game_communication_path, "Required_Assists.cfg"), 'r') as f: + game_values["required_assists"] = int(f.readline()) + else: + game_values["required_assists"] = 0 + if "Required_CS.cfg" in files: + with open(os.path.join(game_communication_path, "Required_CS.cfg"), 'r') as f: + game_values["required_cs"] = int(f.readline()) + else: + game_values["required_cs"] = 0 + if "Required_Kills.cfg" in files: + with open(os.path.join(game_communication_path, "Required_Kills.cfg"), 'r') as f: + game_values["required_kills"] = int(f.readline()) + else: + game_values["required_kills"] = 0 + if "Required_LP.cfg" in files: + with open(os.path.join(game_communication_path, "Required_LP.cfg"), 'r') as f: + game_values["required_lp"] = int(f.readline()) + else: + game_values["required_lp"] = 0 + if "Required_VS.cfg" in files: + with open(os.path.join(game_communication_path, "Required_VS.cfg"), 'r') as f: + game_values["required_vs"] = int(f.readline()) + else: + game_values["required_vs"] = 0 + +def display_champion_list(window): + champion_table_rows = [] + for champion_id in unlocked_champion_ids: + champion_table_rows.append([champions[champion_id]["name"], champion_id]) + window["Champions Unlocked Table"].update(values=champion_table_rows) + +def display_values(window, game_values): + value_table_rows = [] + value_table_rows.append(['Required Kills:' , str(game_values["required_kills"])]) + value_table_rows.append(['Required Assists:' , str(game_values["required_assists"])]) + value_table_rows.append(['Required CS:' , str(game_values["required_cs"])]) + value_table_rows.append(['Required VS:' , str(game_values["required_vs"])]) + value_table_rows.append(['Required LP:' , str(game_values["required_lp"])]) + value_table_rows.append(['Current LP:' , str(game_values["current_lp"])]) + window["Values Table"].update(values=value_table_rows) + +def send_starting_champion_check(): + with open(os.path.join(game_communication_path, "send566000000"), 'w') as f: + f.close() + +def check_lp_for_victory(game_values): + if game_values["current_lp"] >= game_values["required_lp"] and game_values["required_lp"] != 0: + with open(os.path.join(game_communication_path, "victory"), 'w') as f: + f.close() + +def get_player_name(game_data): + return game_data["activePlayer"]["summonerName"].split("#")[0] + +def get_champion_name(game_data, player_name): + for player in game_data["allPlayers"]: + if player["summonerName"] == player_name: + return player["championName"] + +def get_champion_id(champion_name): + for champion_id in champions: + if champions[champion_id]["name"] == champion_name: + return champion_id + +def took_tower(game_data, player_name): + for event in game_data["events"]["Events"]: + if event["EventName"] == "TurretKilled" and event["KillerName"] == player_name: + return True + return False + +def assisted_tower(game_data, player_name): + for event in game_data["events"]["Events"]: + if event["EventName"] == "TurretKilled" and (event["KillerName"] == player_name or player_name in event["Assisters"]): + return True + return False + +def took_inhibitor(game_data, player_name): + for event in game_data["events"]["Events"]: + if event["EventName"] == "InhibKilled" and event["KillerName"] == player_name: + return True + return False + +def assisted_inhibitor(game_data, player_name): + for event in game_data["events"]["Events"]: + if event["EventName"] == "InhibKilled" and (event["KillerName"] == player_name or player_name in event["Assisters"]): + return True + return False + +def took_epic_monster(game_data, player_name, monster_name): + for event in game_data["events"]["Events"]: + if event["EventName"] == monster_name + "Kill" and event["KillerName"] == player_name: + return True + return False + +def assisted_epic_monster(game_data, player_name, monster_name): + for event in game_data["events"]["Events"]: + if event["EventName"] == monster_name + "Kill" and (event["KillerName"] == player_name or player_name in event["Assisters"]): + return True + return False + +def stole_epic_monster(game_data, player_name, monster_name): + for event in game_data["events"]["Events"]: + if event["EventName"] == monster_name + "Kill" and (event["KillerName"] == player_name or player_name in event["Assisters"]) and str(event["Stolen"]) == "True": + return True + return False + +def assisted_kill(game_data, player_name): + for event in game_data["events"]["Events"]: + if event["EventName"] == "ChampionKill" and (event["KillerName"] == player_name or player_name in event["Assisters"]): + return True + return False + +def player_vision_score(game_data, player_name): + for player in game_data["allPlayers"]: + if player["summonerName"] == player_name: + return player["scores"]["wardScore"] + return 0 + +def player_creep_score(game_data, player_name): + for player in game_data["allPlayers"]: + if player["summonerName"] == player_name: + return player["scores"]["creepScore"] + return 0 + +def player_kills(game_data, player_name): + for player in game_data["allPlayers"]: + if player["summonerName"] == player_name: + return player["scores"]["kills"] + return 0 + +def player_assists(game_data, player_name): + for player in game_data["allPlayers"]: + if player["summonerName"] == player_name: + return player["scores"]["assists"] + return 0 + +def vision_score_above(game_data, player_name, score_target): + return player_vision_score(game_data, player_name) >= score_target and score_target > 0 + +def creep_score_above(game_data, player_name, score_target): + return player_creep_score(game_data, player_name) >= score_target and score_target > 0 + +def kills_above(game_data, player_name, score_target): + return player_kills(game_data, player_name) >= score_target and score_target > 0 + +def assists_above(game_data, player_name, score_target): + return player_assists(game_data, player_name) >= score_target and score_target > 0 + +def get_objectives_complete(game_data, game_values): + objectives_complete = [] + player_name = get_player_name(game_data) + champion_name = get_champion_name(game_data, player_name) + champion_id = get_champion_id(champion_name) + if champion_id in unlocked_champion_ids: + if assisted_epic_monster(game_data, player_name, "Dragon"): + objectives_complete.append(1) + if assisted_epic_monster(game_data, player_name, "Herald") or assisted_epic_monster(game_data, player_name, "Horde"): + objectives_complete.append(2) + if assisted_epic_monster(game_data, player_name, "Baron"): + objectives_complete.append(3) + if assisted_tower(game_data, player_name): + objectives_complete.append(4) + if assisted_inhibitor(game_data, player_name): + objectives_complete.append(5) + if assists_above(game_data, player_name, game_values["required_assists"]): + objectives_complete.append(6) + if vision_score_above(game_data, player_name, game_values["required_vs"]): + objectives_complete.append(7) + if kills_above(game_data, player_name, game_values["required_kills"]): + objectives_complete.append(8) + if creep_score_above(game_data, player_name, game_values["required_cs"]): + objectives_complete.append(9) + send_locations(objectives_complete, champion_id) + +def send_locations(objectives_complete, champion_id): + for objective_id in objectives_complete: + with open(os.path.join(game_communication_path, "send" + str(566000000 + (champion_id * 100) + objective_id)), 'w') as f: + f.close() + +sg.theme('DarkAmber') +layout = [ [ + sg.Text('In Match: No', justification = 'center', key = "In Match Text"), + sg.Button('Check for Match', key = "Check for Match Button", disabled_button_color = "blue") + ], + [ + sg.Column( + [ [sg.Text("Champions Unlocked")], + [sg.Table( + [ + ], headings = ["Champion Name", "Champion ID"], key = "Champions Unlocked Table")] + ]), + sg.Column( + [ + [sg.Text("Required Values")], + [sg.Table( + [ + ], headings = ["Value Type", "Value Amount"], key = "Values Table")] + ]) + ] + ] + +window = sg.Window('LOL AP', layout) +while True: + game_data = None + event, values = window.read(timeout=2000) + if event == sg.WIN_CLOSED: + break + if event == 'Check for Match Button': + in_match = True + check_lp_for_victory(game_values) + get_items(game_values) + read_cfg(game_values) + display_champion_list(window) + display_values(window, game_values) + send_starting_champion_check() + if in_match: + game_data = get_game_data() + if game_data is None: + window["In Match Text"].update("In Match: No Match Found") + in_match = False + else: + window["In Match Text"].update("In Match: In Match") + get_objectives_complete(game_data, game_values) + +window.close() \ No newline at end of file diff --git a/worlds/lol/Data.py b/worlds/lol/Data.py index 3b5e6bd85563..2eac2326d869 100644 --- a/worlds/lol/Data.py +++ b/worlds/lol/Data.py @@ -2,51 +2,13 @@ import json versions_url = "https://ddragon.leagueoflegends.com/api/versions.json" -maps_url = "https://static.developer.riotgames.com/docs/lol/maps.json" most_recent_version = requests.get(versions_url).json()[0] -items_url = "https://ddragon.leagueoflegends.com/cdn/" + str(most_recent_version) + "/data/en_US/item.json" -sr_items = {} -aram_items = {} -arena_items = {} -for map in requests.get(maps_url).json(): - if map["mapName"] == "Summoner's Rift" and map["notes"] == "Current Version": - sr_map_id = map["mapId"] - if map["mapName"] == "Howling Abyss": - aram_map_id = map["mapId"] - if map["mapName"] == "Rings of Wrath": - arena_map_id = map["mapId"] +champions_url = "https://ddragon.leagueoflegends.com/cdn/" + str(most_recent_version) + "/data/en_US/champion.json" +champions = {} +champion_data = requests.get(champions_url).json()["data"] -item_data = requests.get(items_url).json()["data"] -for item_id in item_data.keys(): - if "into" not in item_data[item_id].keys() and item_data[item_id]["gold"]["purchasable"] and item_data[item_id]["gold"]["total"] > 1000 and "requiredAlly" not in item_data[item_id].keys(): - if item_data[item_id]["maps"][str(sr_map_id)]: - sr_items[item_id] = item_data[item_id]["name"] - if item_data[item_id]["maps"][str(aram_map_id)]: - aram_items[item_id] = item_data[item_id]["name"] - if item_data[item_id]["maps"][str(arena_map_id)]: - arena_items[item_id] = item_data[item_id]["name"] - - for item_id in item_data.keys(): - if "specialRecipe" in item_data[item_id].keys(): - if str(item_data[item_id]["specialRecipe"]) in sr_items.keys(): - del sr_items[str(item_data[item_id]["specialRecipe"])] - sr_items[item_id] = item_data[item_id]["name"] - if str(item_data[item_id]["specialRecipe"]) in aram_items.keys(): - del aram_items[str(item_data[item_id]["specialRecipe"])] - aram_items[item_id] = item_data[item_id]["name"] - if str(item_data[item_id]["specialRecipe"]) in arena_items.keys(): - del arena_items[str(item_data[item_id]["specialRecipe"])] - arena_items[item_id] = item_data[item_id]["name"] +tags = set([]) +ids = set([]) - #Doing this twice for items that transform twice - for item_id in item_data.keys(): - if "specialRecipe" in item_data[item_id].keys(): - if str(item_data[item_id]["specialRecipe"]) in sr_items.keys(): - del sr_items[str(item_data[item_id]["specialRecipe"])] - sr_items[item_id] = item_data[item_id]["name"] - if str(item_data[item_id]["specialRecipe"]) in aram_items.keys(): - del aram_items[str(item_data[item_id]["specialRecipe"])] - aram_items[item_id] = item_data[item_id]["name"] - if str(item_data[item_id]["specialRecipe"]) in arena_items.keys(): - del arena_items[str(item_data[item_id]["specialRecipe"])] - arena_items[item_id] = item_data[item_id]["name"] \ No newline at end of file +for champion in list(champion_data.keys()): + champions[int(champion_data[champion]["key"])] = champion_data[champion] \ No newline at end of file diff --git a/worlds/lol/Items.py b/worlds/lol/Items.py index 3cb221a5f83d..9b3d2cb7ac42 100644 --- a/worlds/lol/Items.py +++ b/worlds/lol/Items.py @@ -1,5 +1,5 @@ from typing import Dict, NamedTuple, Optional -from .Data import sr_items, aram_items, arena_items +from .Data import champions from BaseClasses import Item, ItemClassification @@ -27,18 +27,9 @@ def get_items_by_category(category: str, disclude: list) -> Dict[str, LOLItemDat item_table: Dict[str, LOLItemData] = {} -for item_id in sr_items: - item_table["SR " + sr_items[item_id]] = LOLItemData("GameMode(Summoners Rift)", code = 5651_000000 + int(item_id), classification = ItemClassification.progression, max_quantity = 1, weight = 1) -for item_id in aram_items: - item_table["ARAM " + aram_items[item_id]] = LOLItemData("GameMode(Aram)", code = 5652_000000 + int(item_id), classification = ItemClassification.progression, max_quantity = 1, weight = 1) -for item_id in arena_items: - item_table["ARENA " + arena_items[item_id]] = LOLItemData("GameMode(Arena)", code = 5653_000000 + int(item_id), classification = ItemClassification.progression, max_quantity = 1, weight = 1) -item_table["Bronze Rank"] = LOLItemData("Victory", code = 5650_000001, classification = ItemClassification.progression, max_quantity = 1, weight = 1) -item_table["Silver Rank"] = LOLItemData("Victory", code = 5650_000002, classification = ItemClassification.progression, max_quantity = 1, weight = 1) -item_table["Gold Rank"] = LOLItemData("Victory", code = 5650_000003, classification = ItemClassification.progression, max_quantity = 1, weight = 1) -item_table["Platinum Rank"] = LOLItemData("Victory", code = 5650_000004, classification = ItemClassification.progression, max_quantity = 1, weight = 1) -item_table["Emerald Rank"] = LOLItemData("Victory", code = 5650_000005, classification = ItemClassification.progression, max_quantity = 1, weight = 1) -item_table["Diamond Rank"] = LOLItemData("Victory", code = 5650_000006, classification = ItemClassification.progression, max_quantity = 1, weight = 1) +for champion_id in champions: + item_table[champions[champion_id]["name"]] = LOLItemData("Champion", code = 565_000000 + int(champion_id), classification = ItemClassification.progression, max_quantity = 1, weight = 1) +item_table["LP"] = LOLItemData("Win Condition", code = 565_000000, classification = ItemClassification.progression, max_quantity = -1, weight = 1) event_item_table: Dict[str, LOLItemData] = { diff --git a/worlds/lol/Locations.py b/worlds/lol/Locations.py index 596760612d76..96255301fa61 100644 --- a/worlds/lol/Locations.py +++ b/worlds/lol/Locations.py @@ -1,5 +1,5 @@ from typing import Dict, NamedTuple, Optional -from .Data import sr_items, aram_items, arena_items +from .Data import champions import typing @@ -25,18 +25,21 @@ def get_locations_by_category(category: str) -> Dict[str, LOLLocationData]: location_table: Dict[str, LOLLocationData] = {} -for item_id in sr_items: - location_table["Win Summoners Rift with " + str(sr_items[item_id])] = LOLLocationData("GameMode(Summoners Rift)", 5661_000000 + int(item_id)) -for item_id in aram_items: - location_table["Win ARAM with " + str(aram_items[item_id])] = LOLLocationData("GameMode(Aram)", 5662_000000 + int(item_id)) -for item_id in arena_items: - location_table["Win Arena with " + str(arena_items[item_id])] = LOLLocationData("GameMode(Arena)", 5663_000000 + int(item_id)) -location_table["Starting Item 1"] = LOLLocationData("Starting" , 5660_000001) -location_table["Starting Item 2"] = LOLLocationData("Starting" , 5660_000002) -location_table["Starting Item 3"] = LOLLocationData("Starting" , 5660_000003) -location_table["Starting Item 4"] = LOLLocationData("Starting" , 5660_000004) -location_table["Starting Item 5"] = LOLLocationData("Starting" , 5660_000005) -location_table["Starting Item 6"] = LOLLocationData("Starting" , 5660_000006) +for champion_id in champions: + champion_name = champions[champion_id]["name"] + location_table["Assist Taking Dragon as " + champion_name] = LOLLocationData("Objective", 566_000000 + (int(champion_id) * 100) + 1) + location_table["Assist Taking Rift Herald as " + champion_name] = LOLLocationData("Objective", 566_000000 + (int(champion_id) * 100) + 2) + location_table["Assist Taking Baron as " + champion_name] = LOLLocationData("Objective", 566_000000 + (int(champion_id) * 100) + 3) + location_table["Assist Taking Tower as " + champion_name] = LOLLocationData("Objective", 566_000000 + (int(champion_id) * 100) + 4) + location_table["Assist Taking Inhibitor as " + champion_name] = LOLLocationData("Objective", 566_000000 + (int(champion_id) * 100) + 5) + location_table["Get X Assists as " + champion_name] = LOLLocationData("Objective", 566_000000 + (int(champion_id) * 100) + 6) + if "Support" in champions[champion_id]["tags"]: + location_table["Get X Ward Score as " + champion_name] = LOLLocationData("Objective", 566_000000 + (int(champion_id) * 100) + 7) + if "Support" not in champions[champion_id]["tags"]: + location_table["Get X Kills as " + champion_name] = LOLLocationData("Objective", 566_000000 + (int(champion_id) * 100) + 8) + location_table["Get X Creep Score as " + champion_name] = LOLLocationData("Objective", 566_000000 + (int(champion_id) * 100) + 9) + +location_table["Starting Champion"] = LOLLocationData("Starting", 566_000000) event_location_table: Dict[str, LOLLocationData] = { } diff --git a/worlds/lol/Options.py b/worlds/lol/Options.py index ff0686d2a450..788fe31300da 100644 --- a/worlds/lol/Options.py +++ b/worlds/lol/Options.py @@ -1,27 +1,69 @@ -from typing import Dict +from dataclasses import dataclass, asdict -from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionSet +from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionSet, PerGameCommonOptions -class GameMode(Choice): +from .Data import champions + + + +class RequiredLPPercentage(Range): + """ + What percentage of total LP available do you need to collect to win? """ - Select game mode to base items/location on. + default = 50 + range_start = 30 + range_end = 100 + display_name = "Required LP Percentage" + +class Champions(OptionSet): + """ + Which champions are included in the item pool? """ - display_name = "Game Mode" - option_summoners_rift = 0 - option_aram = 1 - option_arena = 2 - default = 0 + display_name = "Champions" + valid_keys = [champions[champion_id]["name"] for champion_id in champions] + default = set([champions[champion_id]["name"] for champion_id in champions]) -class ItemNumber(Range): +class RequiredCreepScore(Range): """ - How many items/locations should be included in the pool? + Required CS to complete CS checks + """ + default = 100 + range_start = 50 + range_end = 400 + display_name = "Required Creep Score" + +class RequiredVisionScore(Range): + """ + Required VS to complete VS checks """ default = 30 range_start = 10 - range_end = 60 - display_name = "Number of Items" + range_end = 100 + display_name = "Required Vison Score" + +class RequiredKills(Range): + """ + Required Kills to complete Kill checks + """ + default = 3 + range_start = 1 + range_end = 15 + display_name = "Required Kills" + +class RequiredAssists(Range): + """ + Required Assists to complete Assist checks + """ + default = 5 + range_start = 3 + range_end = 30 + display_name = "Required Assists" -lol_options: Dict[str, type(Option)] = { - "game_mode": GameMode, - "item_num": ItemNumber, -} +@dataclass +class LOLOptions(PerGameCommonOptions): + champions: Champions + required_creep_score: RequiredCreepScore + required_vision_score: RequiredVisionScore + required_kills: RequiredKills + required_assists: RequiredAssists + required_lp: RequiredLPPercentage \ No newline at end of file diff --git a/worlds/lol/Regions.py b/worlds/lol/Regions.py index 899fcb37add2..81ec4ee95ded 100644 --- a/worlds/lol/Regions.py +++ b/worlds/lol/Regions.py @@ -2,7 +2,7 @@ from BaseClasses import MultiWorld, Region, Entrance from .Locations import LOLLocation, location_table, get_locations_by_category -from .Data import sr_items, aram_items, arena_items +from .Data import champions class LOLRegionData(NamedTuple): @@ -10,7 +10,7 @@ class LOLRegionData(NamedTuple): region_exits: Optional[List[str]] -def create_regions(multiworld: MultiWorld, player: int, game_mode: str, items: list[str]): +def create_regions(multiworld: MultiWorld, player: int, options): regions: Dict[str, LOLRegionData] = { "Menu": LOLRegionData(None, ["Match"]), "Match": LOLRegionData([], []), @@ -18,24 +18,22 @@ def create_regions(multiworld: MultiWorld, player: int, game_mode: str, items: l # Set up locations - if game_mode == "GameMode(Summoners Rift)": - for item_id in sr_items: - if "SR " + str(sr_items[item_id]) in items or len(items) == 0: - regions["Match"].locations.append("Win Summoners Rift with " + str(sr_items[item_id])) - if game_mode == "GameMode(Aram)": - for item_id in aram_items: - if "ARAM " + str(aram_items[item_id]) in items or len(items) == 0: - regions["Match"].locations.append("Win ARAM with " + str(aram_items[item_id])) - if game_mode == "GameMode(Arena)": - for item_id in arena_items: - if "ARENA " + str(arena_items[item_id]) in items or len(items) == 0: - regions["Match"].locations.append("Win Arena with " + str(arena_items[item_id])) - regions["Match"].locations.append("Starting Item 1") - regions["Match"].locations.append("Starting Item 2") - regions["Match"].locations.append("Starting Item 3") - regions["Match"].locations.append("Starting Item 4") - regions["Match"].locations.append("Starting Item 5") - regions["Match"].locations.append("Starting Item 6") + for champion_id in champions: + champion_name = champions[champion_id]["name"] + if champion_name in options.champions.value: + regions["Match"].locations.append("Assist Taking Dragon as " + champion_name) + regions["Match"].locations.append("Assist Taking Rift Herald as " + champion_name) + regions["Match"].locations.append("Assist Taking Baron as " + champion_name) + regions["Match"].locations.append("Assist Taking Tower as " + champion_name) + regions["Match"].locations.append("Assist Taking Inhibitor as " + champion_name) + regions["Match"].locations.append("Assist Taking Inhibitor as " + champion_name) + regions["Match"].locations.append("Get X Assists as " + champion_name) + if "Support" in champions[champion_id]["tags"]: + regions["Match"].locations.append("Get X Ward Score as " + champion_name) + if "Support" not in champions[champion_id]["tags"]: + regions["Match"].locations.append("Get X Kills as " + champion_name) + regions["Match"].locations.append("Get X Creep Score as " + champion_name) + regions["Match"].locations.append("Starting Champion") # Set up the regions correctly. for name, data in regions.items(): diff --git a/worlds/lol/Rules.py b/worlds/lol/Rules.py index 3b5e261de4d8..8e32bdd29957 100644 --- a/worlds/lol/Rules.py +++ b/worlds/lol/Rules.py @@ -1,24 +1,29 @@ from BaseClasses import CollectionState, MultiWorld, LocationProgressType from .Locations import get_locations_by_category -from .Data import sr_items, aram_items, arena_items +from .Data import champions def has_item(state: CollectionState, player: int, item) -> bool: return state.has(item, player) -def set_rules(multiworld: MultiWorld, player: int, game_mode: str, items: list[str]): - if game_mode == "GameMode(Summoners Rift)": - for item_id in sr_items: - print(sr_items[item_id]) - if "SR " + str(sr_items[item_id]) in items or len(items) == 0: - multiworld.get_location("Win Summoners Rift with " + str(sr_items[item_id]), player).access_rule = lambda state, id=item_id: has_item(state, player, "SR " + sr_items[id]) - if game_mode == "GameMode(Aram)": - for item_id in aram_items: - if "ARAM " + str(aram_items[item_id]) in items or len(items) == 0: - multiworld.get_location("Win ARAM with " + str(aram_items[item_id]), player).access_rule = lambda state, id=item_id: has_item(state, player, "ARAM " + aram_items[id]) - if game_mode == "GameMode(Arena)": - for item_id in arena_items: - if "ARENA " + str(arena_items[item_id]) in items or len(items) == 0: - multiworld.get_location("Win Arena with " + str(arena_items[item_id]), player).access_rule = lambda state, id=item_id: has_item(state, player, "ARENA " + arena_items[id]) +def has_at_least(state: CollectionState, player: int, item_name, item_qty_required) -> bool: + return state.count(item_name, player) >= item_qty_required + +def set_rules(multiworld: MultiWorld, player: int, options, required_lp): + for champion_id in champions: + champion_name = champions[champion_id]["name"] + if champion_name in options.champions.value: + multiworld.get_location("Assist Taking Dragon as " + champion_name, player).access_rule = lambda state, champion_name = champion_name: has_item(state, player, champion_name) + multiworld.get_location("Assist Taking Rift Herald as " + champion_name, player).access_rule = lambda state, champion_name = champion_name: has_item(state, player, champion_name) + multiworld.get_location("Assist Taking Baron as " + champion_name, player).access_rule = lambda state, champion_name = champion_name: has_item(state, player, champion_name) + multiworld.get_location("Assist Taking Tower as " + champion_name, player).access_rule = lambda state, champion_name = champion_name: has_item(state, player, champion_name) + multiworld.get_location("Assist Taking Inhibitor as " + champion_name, player).access_rule = lambda state, champion_name = champion_name: has_item(state, player, champion_name) + multiworld.get_location("Assist Taking Inhibitor as " + champion_name, player).access_rule = lambda state, champion_name = champion_name: has_item(state, player, champion_name) + multiworld.get_location("Get X Assists as " + champion_name, player).access_rule = lambda state, champion_name = champion_name: has_item(state, player, champion_name) + if "Support" in champions[champion_id]["tags"]: + multiworld.get_location("Get X Ward Score as " + champion_name, player).access_rule = lambda state, champion_name = champion_name: has_item(state, player, champion_name) + if "Support" not in champions[champion_id]["tags"]: + multiworld.get_location("Get X Kills as " + champion_name, player).access_rule = lambda state, champion_name = champion_name: has_item(state, player, champion_name) + multiworld.get_location("Get X Creep Score as " + champion_name, player).access_rule = lambda state, champion_name = champion_name: has_item(state, player, champion_name) # Win condition. - multiworld.completion_condition[player] = lambda state: state.has_all({"Bronze Rank", "Silver Rank", "Gold Rank", "Platinum Rank", "Emerald Rank", "Diamond Rank"}, player) + multiworld.completion_condition[player] = lambda state: has_at_least(state, player, "LP", required_lp) diff --git a/worlds/lol/__init__.py b/worlds/lol/__init__.py index aceca09c5d67..4f75bdf5805c 100644 --- a/worlds/lol/__init__.py +++ b/worlds/lol/__init__.py @@ -4,9 +4,10 @@ from worlds.AutoWorld import WebWorld, World from .Items import LOLItem, LOLItemData, event_item_table, get_items_by_category, item_table from .Locations import LOLLocation, location_table, get_locations_by_category -from .Options import lol_options +from .Options import LOLOptions from .Regions import create_regions from .Rules import set_rules +from .Data import champions from worlds.LauncherComponents import Component, components, Type, launch_subprocess import random @@ -36,62 +37,47 @@ class LOLWorld(World): League of Legends (LoL), commonly referred to as League, is a 2009 multiplayer online battle arena video game developed and published by Riot Games. """ game = "League of Legends" - option_definitions = lol_options + options_dataclass = LOLOptions + options: LOLOptions topology_present = True - data_version = 4 required_client_version = (0, 3, 5) web = LOLWeb() - game_item_table = [] item_name_to_id = {name: data.code for name, data in item_table.items()} location_name_to_id = {name: data.code for name, data in location_table.items()} - # TODO: Replace calls to this function with "options-dict", once that PR is completed and merged. - def get_setting(self, name: str): - return getattr(self.multiworld, name)[self.player] - - def fill_slot_data(self) -> dict: - return {option_name: self.get_setting(option_name).value for option_name in lol_options} - def create_items(self): - self.set_item_table() - starting_locations = list(get_locations_by_category("Starting").keys()) - starting_items = random.sample([item for item in self.game_item_table if not item.endswith("Rank")], 6) - i = 0 - while i < 6: - self.multiworld.get_location(starting_locations[i], self.player).place_locked_item(self.create_item(starting_items[i])) - i = i + 1 + print(self.options.champions.value) + possible_champions = [] + for champion_id in champions: + champion_name = champions[champion_id]["name"] + if champion_name in self.options.champions.value: + possible_champions.append(champion_name) + starting_champion = random.choice(possible_champions) + self.multiworld.get_location("Starting Champion", self.player).place_locked_item(self.create_item(starting_champion)) + total_locations = len(self.multiworld.get_unfilled_locations(self.player)) item_pool: List[LOLItem] = [] - for name in self.game_item_table: - if name not in starting_items: + for name, data in item_table.items(): + if name in possible_champions and name != starting_champion: item_pool += [self.create_item(name) for _ in range(0, 1)] - + while len(item_pool) < total_locations: + item_pool.append(self.create_item("LP")) self.multiworld.itempool += item_pool - - def get_filler_item_name(self) -> str: - fillers = {} - disclude = [] - fillers.update(get_items_by_category("Item", disclude)) - weights = [data.weight for data in fillers.values()] - return self.multiworld.random.choices([filler for filler in fillers.keys()], weights, k=1)[0] def create_item(self, name: str) -> LOLItem: data = item_table[name] return LOLItem(name, data.classification, data.code, self.player) - def create_event(self, name: str) -> LOLItem: - data = event_item_table[name] - return LOLItem(name, data.classification, data.code, self.player) - def set_rules(self): - set_rules(self.multiworld, self.player, str(self.get_setting("game_mode")), self.game_item_table) + set_rules(self.multiworld, self.player, self.options, int((len(self.multiworld.itempool) - len(self.options.champions.value)) * (self.options.required_lp / 100))) def create_regions(self): - self.set_item_table() - create_regions(self.multiworld, self.player, str(self.get_setting("game_mode")), self.game_item_table) + create_regions(self.multiworld, self.player, self.options) - def set_item_table(self): - if len(self.game_item_table) == 0: - self.game_item_table = get_items_by_category(str(self.get_setting("game_mode")), []).keys() - self.game_item_table = random.sample(list(self.game_item_table), int(self.get_setting("item_num"))) - self.game_item_table = self.game_item_table + list(get_items_by_category("Victory", []).keys()) \ No newline at end of file + def fill_slot_data(self) -> dict: + slot_data = {"Required CS": int(self.options.required_creep_score) + ,"Required VS": int(self.options.required_vision_score) + ,"Required Kills": int(self.options.required_kills) + ,"Required Assists": int(self.options.required_assists) + ,"Required LP": int((len(self.multiworld.itempool) - len(self.options.champions.value)) * (self.options.required_lp / 100))} + return slot_data \ No newline at end of file