diff --git a/KH2Client.py b/KH2Client.py index 5223d8a111f7..737353726474 100644 --- a/KH2Client.py +++ b/KH2Client.py @@ -53,79 +53,8 @@ def __init__(self, server_address, password): self.collectible_override_flags_address = 0 self.collectible_offsets = {} self.sending = [] - # flag for if the player has gotten their starting inventory from the server - self.hasStartingInvo = False # list used to keep track of locations+items player has. Used for disoneccting - self.kh2seedsave = {"checked_locations": {"0": []}, - "starting_inventory": self.hasStartingInvo, - - # Character: [back of invo, front of invo] - "SoraInvo": [0x25CC, 0x2546], - "DonaldInvo": [0x2678, 0x2658], - "GoofyInvo": [0x278E, 0x276C], - "AmountInvo": { - "ServerItems": { - "Ability": {}, - "Amount": {}, - "Growth": {"High Jump": 0, "Quick Run": 0, "Dodge Roll": 0, "Aerial Dodge": 0, - "Glide": 0}, - "Bitmask": [], - "Weapon": {"Sora": [], "Donald": [], "Goofy": []}, - "Equipment": [], - "Magic": {}, - "StatIncrease": {}, - "Boost": {}, - }, - "LocalItems": { - "Ability": {}, - "Amount": {}, - "Growth": {"High Jump": 0, "Quick Run": 0, "Dodge Roll": 0, - "Aerial Dodge": 0, "Glide": 0}, - "Bitmask": [], - "Weapon": {"Sora": [], "Donald": [], "Goofy": []}, - "Equipment": [], - "Magic": {}, - "StatIncrease": {}, - "Boost": {}, - }}, - # 1,3,255 are in this list in case the player gets locations in those "worlds" and I need to still have them checked - "worldIdChecks": { - "1": [], # world of darkness (story cutscenes) - "2": [], - "3": [], # destiny island doesn't have checks to ima put tt checks here - "4": [], - "5": [], - "6": [], - "7": [], - "8": [], - "9": [], - "10": [], - "11": [], - # atlantica isn't a supported world. if you go in atlantica it will check dc - "12": [], - "13": [], - "14": [], - "15": [], - # world map, but you only go to the world map while on the way to goa so checking hb - "16": [], - "17": [], - "18": [], - "255": [], # starting screen - }, - "Levels": { - "SoraLevel": 0, - "ValorLevel": 0, - "WisdomLevel": 0, - "LimitLevel": 0, - "MasterLevel": 0, - "FinalLevel": 0, - }, - "SoldEquipment": [], - "SoldBoosts": {"Power Boost": 0, - "Magic Boost": 0, - "Defense Boost": 0, - "AP Boost": 0} - } + self.kh2seedsave = None self.slotDataProgressionNames = {} self.kh2seedname = None self.kh2slotdata = None @@ -202,14 +131,13 @@ def __init__(self, server_address, password): self.boost_set = set(CheckDupingItems["Boosts"]) self.stat_increase_set = set(CheckDupingItems["Stat Increases"]) - self.AbilityQuantityDict = {item: self.item_name_to_data[item].quantity for item in self.all_abilities} # Growth:[level 1,level 4,slot] - self.growth_values_dict = {"High Jump": [0x05E, 0x061, 0x25CE], - "Quick Run": [0x62, 0x65, 0x25D0], - "Dodge Roll": [0x234, 0x237, 0x25D2], - "Aerial Dodge": [0x066, 0x069, 0x25D4], - "Glide": [0x6A, 0x6D, 0x25D6]} + self.growth_values_dict = {"High Jump": [0x05E, 0x061, 0x25DA], + "Quick Run": [0x62, 0x65, 0x25DC], + "Dodge Roll": [0x234, 0x237, 0x25DE], + "Aerial Dodge": [0x066, 0x069, 0x25E0], + "Glide": [0x6A, 0x6D, 0x25E2]} self.boost_to_anchor_dict = { "Power Boost": 0x24F9, "Magic Boost": 0x24FA, @@ -269,19 +197,66 @@ def on_package(self, cmd: str, args: dict): if not os.path.exists(self.game_communication_path): os.makedirs(self.game_communication_path) if not os.path.exists(self.game_communication_path + f"\kh2save{self.kh2seedname}{self.auth}.json"): + self.kh2seedsave = {"itemIndex": -1, + # back of soras invo is 0x25E2. Growth should be moved there + # Character: [back of invo, front of invo] + "SoraInvo": [0x25D8, 0x2546], + "DonaldInvo": [0x26F4, 0x2658], + "GoofyInvo": [0x280A, 0x276C], + "AmountInvo": { + "ServerItems": { + "Ability": {}, + "Amount": {}, + "Growth": {"High Jump": 0, "Quick Run": 0, "Dodge Roll": 0, + "Aerial Dodge": 0, + "Glide": 0}, + "Bitmask": [], + "Weapon": {"Sora": [], "Donald": [], "Goofy": []}, + "Equipment": [], + "Magic": {}, + "StatIncrease": {}, + "Boost": {}, + }, + "LocalItems": { + "Ability": {}, + "Amount": {}, + "Growth": {"High Jump": 0, "Quick Run": 0, "Dodge Roll": 0, + "Aerial Dodge": 0, "Glide": 0}, + "Bitmask": [], + "Weapon": {"Sora": [], "Donald": [], "Goofy": []}, + "Equipment": [], + "Magic": {}, + "StatIncrease": {}, + "Boost": {}, + }}, + # 1,3,255 are in this list in case the player gets locations in those "worlds" and I need to still have them checked + "LocationsChecked": [], + "Levels": { + "SoraLevel": 0, + "ValorLevel": 0, + "WisdomLevel": 0, + "LimitLevel": 0, + "MasterLevel": 0, + "FinalLevel": 0, + }, + "SoldEquipment": [], + "SoldBoosts": {"Power Boost": 0, + "Magic Boost": 0, + "Defense Boost": 0, + "AP Boost": 0} + } with open(os.path.join(self.game_communication_path, f"kh2save{self.kh2seedname}{self.auth}.json"), 'wt') as f: pass + self.locations_checked = set() elif os.path.exists(self.game_communication_path + f"\kh2save{self.kh2seedname}{self.auth}.json"): with open(self.game_communication_path + f"\kh2save{self.kh2seedname}{self.auth}.json", 'r') as f: self.kh2seedsave = json.load(f) + self.locations_checked = set(self.kh2seedsave["LocationsChecked"]) + self.serverconneced = True if cmd in {"Connected"}: - for player in args['players']: - if str(player.slot) not in self.kh2seedsave["checked_locations"]: - self.kh2seedsave["checked_locations"].update({str(player.slot): []}) self.kh2slotdata = args['slot_data'] - self.serverconneced = True self.kh2LocalItems = {int(location): item for location, item in self.kh2slotdata["LocalItems"].items()} try: self.kh2 = pymem.Pymem(process_name="KINGDOM HEARTS II FINAL MIX") @@ -296,21 +271,10 @@ def on_package(self, cmd: str, args: dict): if cmd in {"ReceivedItems"}: start_index = args["index"] - if start_index != len(self.items_received): + if start_index > self.kh2seedsave["itemIndex"]: + self.kh2seedsave["itemIndex"] = start_index for item in args['items']: - # starting invo from server - if item.location in {-2}: - if not self.kh2seedsave["starting_inventory"]: - asyncio.create_task(self.give_item(item.item)) - # if location is not already given or is !getitem - elif item.location not in self.kh2seedsave["checked_locations"][str(item.player)] \ - or item.location in {-1}: - asyncio.create_task(self.give_item(item.item)) - if item.location not in self.kh2seedsave["checked_locations"][str(item.player)] \ - and item.location not in {-1, -2}: - self.kh2seedsave["checked_locations"][str(item.player)].append(item.location) - if not self.kh2seedsave["starting_inventory"]: - self.kh2seedsave["starting_inventory"] = True + asyncio.create_task(self.give_item(item.item)) if cmd in {"RoomUpdate"}: if "checked_locations" in args: @@ -326,12 +290,13 @@ async def checkWorldLocations(self): if currentworldint in self.worldid: curworldid = self.worldid[currentworldint] for location, data in curworldid.items(): - if location not in self.locations_checked \ + locationId = kh2_loc_name_to_id[location] + if locationId not in self.locations_checked \ and (int.from_bytes( self.kh2.read_bytes(self.kh2.base_address + self.Save + data.addrObtained, 1), "big") & 0x1 << data.bitIndex) > 0: - self.locations_checked.add(location) - self.sending = self.sending + [(int(kh2_loc_name_to_id[location]))] + + self.sending = self.sending + [(int(locationId))] except Exception as e: logger.info("Line 285") if self.kh2connected: @@ -344,12 +309,12 @@ async def checkLevels(self): for location, data in SoraLevels.items(): currentLevel = int.from_bytes( self.kh2.read_bytes(self.kh2.base_address + self.Save + 0x24FF, 1), "big") - if location not in self.locations_checked \ + locationId = kh2_loc_name_to_id[location] + if locationId not in self.locations_checked \ and currentLevel >= data.bitIndex: if self.kh2seedsave["Levels"]["SoraLevel"] < currentLevel: self.kh2seedsave["Levels"]["SoraLevel"] = currentLevel - self.locations_checked.add(location) - self.sending = self.sending + [(int(kh2_loc_name_to_id[location]))] + self.sending = self.sending + [(int(locationId))] formDict = { 0: ["ValorLevel", ValorLevels], 1: ["WisdomLevel", WisdomLevels], 2: ["LimitLevel", LimitLevels], 3: ["MasterLevel", MasterLevels], 4: ["FinalLevel", FinalLevels]} @@ -357,12 +322,12 @@ async def checkLevels(self): for location, data in formDict[i][1].items(): formlevel = int.from_bytes( self.kh2.read_bytes(self.kh2.base_address + self.Save + data.addrObtained, 1), "big") - if location not in self.locations_checked \ + locationId = kh2_loc_name_to_id[location] + if locationId not in self.locations_checked \ and formlevel >= data.bitIndex: if formlevel > self.kh2seedsave["Levels"][formDict[i][0]]: self.kh2seedsave["Levels"][formDict[i][0]] = formlevel - self.locations_checked.add(location) - self.sending = self.sending + [(int(kh2_loc_name_to_id[location]))] + self.sending = self.sending + [(int(locationId))] except Exception as e: logger.info("Line 312") if self.kh2connected: @@ -373,18 +338,21 @@ async def checkLevels(self): async def checkSlots(self): try: for location, data in weaponSlots.items(): - if location not in self.locations_checked: + locationId = kh2_loc_name_to_id[location] + if locationId not in self.locations_checked: if int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + data.addrObtained, 1), "big") > 0: - self.locations_checked.add(location) - self.sending = self.sending + [(int(kh2_loc_name_to_id[location]))] + + self.sending = self.sending + [(int(locationId))] for location, data in formSlots.items(): - if location not in self.locations_checked: + locationId = kh2_loc_name_to_id[location] + if locationId not in self.locations_checked: if int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + data.addrObtained, 1), "big") & 0x1 << data.bitIndex > 0: - self.locations_checked.add(location) - self.sending = self.sending + [(int(kh2_loc_name_to_id[location]))] + #self.locations_checked + self.sending = self.sending + [(int(locationId))] + except Exception as e: if self.kh2connected: logger.info("Line 333") @@ -394,8 +362,7 @@ async def checkSlots(self): async def verifyChests(self): try: - currentworld = str(int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + 0x0714DB8, 1), "big")) - for location in self.kh2seedsave["worldIdChecks"][currentworld]: + for location in self.locations_checked: locationName = self.lookup_id_to_Location[location] if locationName in self.chest_set: if locationName in self.location_name_to_worlddata.keys(): @@ -428,24 +395,6 @@ async def verifyLevel(self): self.kh2.write_bytes(self.kh2.base_address + self.Save + anchor, (self.kh2seedsave["Levels"][leveltype]).to_bytes(1, 'big'), 1) - def verifyLocation(self, location): - locationData = self.location_name_to_worlddata[location] - locationName = self.lookup_id_to_Location[location] - isChecked = True - - if locationName not in levels_locations: - if (int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + locationData.addrObtained, 1), - "big") & 0x1 << locationData.bitIndex) == 0: - isChecked = False - elif locationName in SoraLevels: - if int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + 0x24FF, 1), - "big") < locationData.bitIndex: - isChecked = False - elif int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + locationData.addrObtained, 1), - "big") < locationData.bitIndex: - isChecked = False - return isChecked - async def give_item(self, item, ItemType="ServerItems"): try: itemname = self.lookup_id_to_item[item] @@ -680,6 +629,17 @@ async def verifyItems(self): ability = current & 0x0FFF if ability | 0x8000 != (0x8000 + itemData.memaddr): self.kh2.write_short(self.kh2.base_address + self.Save + slot, itemData.memaddr) + # removes the duped ability if client gave faster than the game. + for charInvo in {"SoraInvo", "DonaldInvo", "GoofyInvo"}: + if self.kh2.read_short(self.kh2.base_address + self.Save + self.kh2seedsave[charInvo][1]) != 0 and\ + self.kh2seedsave[charInvo][1]+2 < self.kh2seedsave[charInvo][0]: + self.kh2.write_short(self.kh2.base_address + self.Save + self.kh2seedsave[charInvo][1], 0) + # remove the dummy level 1 growths if they are in these invo slots. + for inventorySlot in {0x25CE, 0x25D0, 0x25D2, 0x25D4, 0x25D6, 0x25D8}: + current = self.kh2.read_short(self.kh2.base_address + self.Save + inventorySlot) + ability = current & 0x0FFF + if 0x05E <= ability <= 0x06D: + self.kh2.write_short(self.kh2.base_address + self.Save + inventorySlot, 0) for itemName in self.master_growth: growthLevel = self.kh2seedsave["AmountInvo"]["ServerItems"]["Growth"][itemName] \ @@ -753,10 +713,11 @@ async def verifyItems(self): if itemName in server_stat: amountOfItems += self.kh2seedsave["AmountInvo"]["ServerItems"]["StatIncrease"][itemName] + # 0x130293 is Crit_1's location id for touching the computer if int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Save + itemData.memaddr, 1), "big") != amountOfItems \ and int.from_bytes(self.kh2.read_bytes(self.kh2.base_address + self.Slot1 + 0x1B2, 1), - "big") >= 5: + "big") >= 5 and 0x130293 in self.locations_checked: self.kh2.write_bytes(self.kh2.base_address + self.Save + itemData.memaddr, amountOfItems.to_bytes(1, 'big'), 1) @@ -859,9 +820,9 @@ async def kh2_watcher(ctx: KH2Context): location_ids = [] location_ids = [location for location in message[0]["locations"] if location not in location_ids] for location in location_ids: - currentWorld = int.from_bytes(ctx.kh2.read_bytes(ctx.kh2.base_address + 0x0714DB8, 1), "big") - if location not in ctx.kh2seedsave["worldIdChecks"][str(currentWorld)]: - ctx.kh2seedsave["worldIdChecks"][str(currentWorld)].append(location) + if location not in ctx.locations_checked: + ctx.locations_checked.add(location) + ctx.kh2seedsave["LocationsChecked"].append(location) if location in ctx.kh2LocalItems: item = ctx.kh2slotdata["LocalItems"][str(location)] await asyncio.create_task(ctx.give_item(item, "LocalItems"))