def randomizeWorldRoutes(): if shouldIRandomize: file = "00progress.bin" if (copyKHFile(file)): fileBin = open(file, "rb+") writeRandomizationLog(file) if (fileBin): findHeaderinBAR(fileBin, 'wldf', True) fileBin.seek(0x4, 1) #skip a number
def openKHText(file): #ex : file:msg/jp/tt.bar #open file, grab all strings, then when we finish modifying all the strings, get the length of them all and replace. #Unordered list with ID and Position. fileBin = open(file, 'rb+') offset = findBarHeader(fileBin) test = ( file[len(file) - 4 - 2:] )[: 2] #Find world name by removing the last 4 characters and subtracting the length to start at the world name findHeaderinBAR(fileBin, test, False) fileBin.seek(0x8, 1) # skip header thing stringSize = readAndUnpack(fileBin, 4) findHeaderinBAR(fileBin, test, True) startingStringTablePos = fileBin.tell() fileBin.seek(0x4, 1) #skip header thing amountOfTextLines = readAndUnpack(fileBin, 4) stringList = [] for x in range(amountOfTextLines): fileBin.seek(startingStringTablePos + 8 + (x * 8), 0) stringID = readAndUnpack(fileBin, 4) stringPos = readAndUnpack(fileBin, 4) filterStuff = list( filter(lambda x: (stringPos == x.position), stringList)) if len(filterStuff) != 0: filterStuff[0].addID(stringID) else: stringList.append(TextBar(stringID, stringPos, '')) stringList = sorted(stringList, key=lambda x: x.position) fileBin.seek(startingStringTablePos + 8 + (amountOfTextLines * 8), 0) for x in range(len(stringList)): stringData = [] currentChar = -1 currentNextID = 1 if x < len(stringList) - 1: lengthOfString = (stringList[x + currentNextID].position - stringList[x].position) while lengthOfString > 0: currentChar = readAndUnpack(fileBin, 1) stringData.append(currentChar) lengthOfString -= 1 else: lengthOfString = ((stringSize) - stringList[x].position) while lengthOfString > 0: currentChar = readAndUnpack(fileBin, 1) stringData.append(currentChar) lengthOfString -= 1 stringList[x].text = stringData fileBin.close() return stringList
def closeKHText(file, strings): fileBin = open(file, 'rb+') offset = findBarHeader(fileBin) findHeaderinBAR(fileBin, 'md_m', False) fileBin.seek(0x8, 1) md_m_size = readAndUnpack(fileBin, 4) findHeaderinBAR(fileBin, 'md_m', True) md_m_Data = fileBin.read(md_m_size) miscData = fileBin.read() test = ( file[len(file) - 4 - 2:] )[: 2] # Find world name by removing the last 4 characters and subtracting the length to start at the world name findHeaderinBAR(fileBin, test, True) startingStringTablePos = fileBin.tell() fileBin.seek(0x4, 1) # skip header thing amountOfTextLines = readAndUnpack(fileBin, 4) startReadingPos = fileBin.tell() fileBin.seek(startingStringTablePos + 8 + (amountOfTextLines * 8), 0) for x in strings: x.position = fileBin.tell() - startingStringTablePos newList = x.writeBackToFile() for character in newList: writeIntOrHex(fileBin, character, 1) fileEndSize = fileBin.tell() - offset fileBin.truncate(fileEndSize) newTextBarFileSize = fileEndSize - startingStringTablePos while fileBin.tell() % 16 != 0: writeIntOrHex(fileBin, 0, 1) newMd_mPos = fileBin.tell() - offset fileBin.write(md_m_Data) ModifyExtraFilePosition( fileBin, fileBin.tell()) #we want relative positions to 0,0 of the file. fileBin.write(miscData) fileBin.seek(startReadingPos, 0) #Go back to the start with ids and positions for x in strings: stringID = readAndUnpack(fileBin, 4) filterStuff = list(filter(lambda x: (stringID in x.ids), strings)) writeIntOrHex(fileBin, filterStuff[0].position, 4) #Write new positions of text findHeaderinBAR(fileBin, 'md_m', False) fileBin.seek(0x4, 1) writeIntOrHex(fileBin, newMd_mPos, 4) findHeaderinBAR(fileBin, test, False) fileBin.seek(0x8, 1) writeIntOrHex(fileBin, newTextBarFileSize, 4) fileBin.close()
def skipGummiShipMissions(doItOrNot): #set roxas and kh1 sora to kh2 sora if doItOrNot: print("Skipping gummiship missions...") if copyKHFile("00progress.bin"): fileBin = open("00progress.bin", 'rb+') if (fileBin): skipCode = [ 0x04, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00 ] findHeaderinBAR(fileBin, 'wldf', True) if PS3Version(): skipCode.insert(0, 0x00) fileBin.seek(0x9b4, 1) fileBin.write(bytearray(skipCode)) print("Done!") fileBin.close()
def RandomizeCriticalBonusAbilities(shouldIrandomize, shouldIrandomize2): offSetSeed(5) if (shouldIrandomize and shouldIrandomize2): print("Modifying critical bonus abilities...") if (copyKHFile("00battle.bin")): fileBin = open("00battle.bin", 'rb+') findHeaderinBAR( fileBin, 'plrp', True) # skip to nearest bar closest to where we want to go if (fileBin): fileBin.seek(0x1f94, 1) #skip to critical mode abilities currentPos = fileBin.tell() critBonusNum = 300 for i in range(7): fileBin.seek(currentPos + (i * 2), 0) OldAbility = readAndUnpack(fileBin, 2) fileBin.seek(-2, 1) # Go bak if OldAbility != 0x194: #Do not randomize no exp newList = list( filter(lambda d: d.character == KHCharacter.Sora, AbilityList[AbilityTable_enum.Support]) ) # Check for same character newList = list( filter( lambda d: d.abilityLearned == AbilityTypeGained .CriticalBonus, newList)) newList = list(filter(lambda d: d.emptyItem(), newList)) if len(newList) != 0: random.shuffle(newList) fromNewList = AbilityList[ AbilityTable_enum.Support].index(newList[0]) AbilityList[AbilityTable_enum. Support][fromNewList].subtractQOne() writeIntOrHex( fileBin, AbilityList[AbilityTable_enum.Support] [fromNewList].code, 2) writeOutCome_BonusLevel( KHCharacter.Sora, critBonusNum, AbilityList[AbilityTable_enum.Support] [fromNewList].name) critBonusNum += 1
def shopItemChanging(): findHeaderinBAR(fileBin, 'shop', True) itemsToChange = int( 0x3a0 / 2) #Size divided by entry size to get total entries fileBin.seek( 0xC70, 1 ) #skip all this data i dont know about and go to items for shop_item in range(itemsToChange): CurrentList = list( filter(lambda x: (x.emptyItem()), ItemList_Shop)) previousItem = readAndUnpack(fileBin, 2) fileBin.seek(-2, 1) chooseShopItem = random.choice(CurrentList) ItemList_Shop[ItemList_Shop.index( chooseShopItem)].subtractQOne() writeIntOrHex(fileBin, chooseShopItem.codeReturn(), 2) writeOutCome_ItemShop( shop_item, "OLD: " + "{:<20}".format( findItemInList(ItemList_Shop, previousItem).name) + "NEW:" + "{:<20}".format(chooseShopItem.name))
def giveHUDElements(): pass #Don't do this for now, it sets all battle levels of worlds to lvl 1 which makes things too easy. """ progressBin data bar file inside bar file == so it has its own header with positions and such 0C ?? ?? ?? == Plays or starts a event of some kind? Always starts with 0C. May have something like a prefix 0C 01 91 08 -- Start playing as KH1 Sora 01 == Event Type? 01 or 02 and sometimes 0? 91 == Event ID These event Id's dont show up anywhere else i can find ATM so i have no idea on how to manipluate these into not removing the hud elements These events when triggered also seem to affect the save data part of RAM """ print("Giving hud elements early...") if copyKHFile("00progress.bin"): fileBin = open("00progress.bin", 'rb+') if (fileBin): hudCode = [0x00] findHeaderinBAR(fileBin, 'tt', True) fileBin.seek(0x6fa, 1) fileBin.write(bytearray(hudCode)) fileBin.close() print("Done!")
def itemShopBuyingSellingPrices(): findHeaderinBAR(fileBin, 'item', True) fileBin.seek(0x4, 1) itemsToChange = readAndUnpack(fileBin, 4) OrgPos = fileBin.tell() for item in range(itemsToChange): fileBin.seek(OrgPos + (item * 0x18), 0) itemID = readUnpackGoBack(fileBin, 2) fileBin.seek(0xC, 1) BuyPriceExisting = readUnpackGoBack(fileBin, 2) #loop through and find item in list newItem = -1 for item_x in ItemList_Shop: if item_x.codeReturn() == itemID: newItem = item_x break if newItem != -1 and item_x.shopPrice != -1: writeIntOrHex(fileBin, item_x.shopPrice, 2) #Write new shop buy money writeIntOrHex( fileBin, int(item_x.shopPrice * 0.50), 2 ) #Write new shop sell money times 0.50 of buy price
def FixMulanMSNSoftlock(): findHeaderinBAR(fileBin, 'btl', True) stringToFind = ReverseEndianString('MU02_MS103B', 2, True,True).encode() posBefore = fileBin.tell() readData = fileBin.read() newPos = readData.find(stringToFind) fileBin.seek(posBefore + newPos, 0) #MSNFile text ID location : 0x14 from the first position == textid #MSNFile Help/Continue Pause button toggler: 0x16 from first position and needs to be 0x18 to work """ What mission file seems to determine: First Mission Bar Entry: If enemies should drop items How pausing should work What text ID to display on the information tab Second Mission Bar Entry: ??? Probably controls when enemies should attack during the camera movement phase Anything Else after these two entries: Camera Movement & Panning at the start of the mission Camera Movement & Panning when a boss dies or something fails The graphical effect when you defeat a boss ^ intrestingly unique for many fights. Take a look at Lingering Will's defeat camera and effect VS when roxas is defeated What images should be displayed EX: Luxord time bar, Mulan's Heartless Count and Motivation bar Seems like how enemies should spawn in is determined by ARD files not by MSN files MSN Files are loaded through reference in the ard file from 'BTL' and usually is assigned to an entry starting with 'b_' """ newMSNFile_Location = msnFileCreate_NonBoss('MU02_MS103A') fileBin.write(ReverseEndianString('MU02_MS103A_R', 2, True, True).encode()) MsnFileBin = open(newMSNFile_Location, 'rb+') findHeaderinBAR(MsnFileBin, 'MU02', True) MsnFileBin.seek(0x38, 1) writeIntOrHex(MsnFileBin, 0x0C, 4) MsnFileBin.close()
def ReduceFormGrinding(shouldIreduce): if (shouldIreduce): print("Reducing form grinding...") if (copyKHFile("00battle.bin")): fileBin = open("00battle.bin", 'rb+') # skip 4 bytes findHeaderinBAR(fileBin, 'fmlv', True) # skip 4 bytes if (fileBin): fileBin.seek(0x8, 1) #skip foward 8 for formType in range(5): for formLevel in range(6): levelUpsStartPos = fileBin.tell( ) # gimmie our currentPosition fileBin.seek(0x1, 1) # skip level byte, we know it already FormLVL = readAndUnpack(fileBin, 1) AbilityFormLvlUp = readAndUnpack(fileBin, 2) FormLVLUpXP = readAndUnpack(fileBin, 4) #times 0.40 drive xp FormLVLUpXP = math.floor(FormLVLUpXP * 0.37) fileBin.seek(-4, 1) writeIntOrHex(fileBin, FormLVLUpXP, 4) fileBin.seek(0x8, 1) fileBin.close()
def RandomizeDriveFormAbilities(shouldIrandomize): offSetSeed(4) if (shouldIrandomize): print("Modifying drive form abilities...") if (copyKHFile("00battle.bin")): fileBin = open("00battle.bin", 'rb+') # skip 4 bytes findHeaderinBAR(fileBin, 'fmlv', True) # skip 4 bytes if (fileBin): fileBin.seek(0x8, 1) #skip foward 8 formData = [] filePos = fileBin.tell() #Read for formType in FormTypeEnum: formData.append([]) for formLevel in range(7): formData[formType].append([]) fileBin.seek(0x2, 1) # skip level byte, we know it already AbilityFormLvlUp = readAndUnpack(fileBin, 2) fileBin.seek(0x4, 1) formData[formType][formLevel] = AbilityFormLvlUp #Modify formBonusNum = 200 AutoAbility = {0x181, 0x183, 0x184} randomFormType = list(FormTypeEnum) random.shuffle(randomFormType) randomFormData = list(range(len(formData[formType]))) random.shuffle(randomFormData) for formType in randomFormType: for dt in randomFormData: newList = list( filter(lambda d: d.character == KHCharacter.Sora, AbilityList[AbilityTable_enum.Support]) ) # Check for same character newList = list( filter( lambda d: d.abilityLearned == AbilityTypeGained .DriveForm, newList)) newList = list( filter(lambda d: d.emptyItem(), newList)) # Check if empty ability random.shuffle(newList) if formType in range(1, 6): if formData[formType][dt] != 0: name = "" if formData[formType][dt] == 0x181: name = 'Auto Valor' if formData[formType][dt] == 0x183: name = 'Auto Master' if formData[formType][dt] == 0x184: name = 'Auto Final' if formData[formType][dt] not in AutoAbility: if len(newList) != 0: fromNewList = AbilityList[ AbilityTable_enum.Support].index( newList[0]) AbilityList[AbilityTable_enum.Support][ fromNewList].subtractQOne() formData[formType][dt] = AbilityList[ AbilityTable_enum. Support][fromNewList].code name = AbilityList[ AbilityTable_enum. Support][fromNewList].name else: name = 'None.' formData[formType][dt] = 0 writeOutCome_BonusLevel( KHCharacter.Sora, formBonusNum, str(formType.name) + ":" + name) formBonusNum += 1 #Write fileBin.seek(filePos, 0) for formType in FormTypeEnum: formData.append([]) for formLevel in range(7): formData[formType].append([]) fileBin.seek(0x2, 1) # skip level byte, we know it already AbilityFormLvlUp = writeIntOrHex( fileBin, formData[formType][formLevel], 2) fileBin.seek(0x4, 1) fileBin.close()
def RandomizeChestContents(randomizeChests): offSetSeed(0) if (randomizeChests): print("Randomizing chests...") if (copyKHFile("03system.bin")): fileBin = open("03system.bin", "rb+") if (fileBin): TreasureFilePosition = 0 TreasureFileSize = 0 """So the way this works is that there is a value of hex inside 03system.bin that has the fileposition of the treasure. we navigate to it and get the number of chests in the position in decimal. hex editor displays hex backwards for some odd reason and makes reading pure hex annoying more info:doing file.read(2) in binary files such as done below will be the equivalent of reading 0000 reading 1 for 00 """ findHeaderinBAR(fileBin, 'trsr', True) TreasureFilePosition = fileBin.tell() noChests = 0 UniqueChestID = 0 #We're going to make our own unique chest id cause I don't trust KH2 at all. fileBin.seek(2, 1) #TrsrMagic noChests = readAndUnpack(fileBin, 2) #noChests itemDict = Vividict() # because im dumb lets make a dictonary with unique chest item ids for i in range(noChests + 1): fileBin.seek(TreasureFilePosition + 4 + 2 + (i * 0xC), 0) Item = readAndUnpack( fileBin, 2) #Item ID that the chest currently contains. Type = readHex( fileBin, 1) #Type of item it is. 0 for chest, 1 for Event. WorldID = readHex( fileBin, 1 ) #Which world is it? we can get that chest info here. RoomIndex = readHex( fileBin, 1) #Which room is it? we can get that chest info here. RoomChestIndex = readHex( fileBin, 1 ) #Which roomchestindex is it? we can get that chest info here. EventID = readHex(fileBin, 2) itemDict[WorldID][RoomIndex][RoomChestIndex][Type][ EventID][Item] = UniqueChestID UniqueChestID += 1 #because im dumb lets make a dictonary with unique chest item ids randomChestLoop = list(range(noChests + 1)) random.shuffle( randomChestLoop ) #We will randomize the order we go through chests in in order to be a bit more random. Kinda odd but yea so we evenly spread out priority items instead of like in a row. for i in randomChestLoop: Item = 0 Type = 0 fileBin.seek(TreasureFilePosition + 4 + 2 + (i * 0xC), 0) Item = readAndUnpack( fileBin, 2) #Item ID that the chest currently contains. Type = readHex( fileBin, 1) #Type of item it is. 0 for chest, 1 for Event. WorldID = readHex( fileBin, 1 ) #Which world is it? we can get that chest info here. RoomIndex = readHex( fileBin, 1) #Which room is it? we can get that chest info here. RoomChestIndex = readHex( fileBin, 1 ) #Which roomchestindex is it? we can get that chest info here. EventID = readHex(fileBin, 2) UniqueChestID = itemDict[WorldID][RoomIndex][ RoomChestIndex][Type][EventID][Item] fileBin.seek( TreasureFilePosition + 4 + 2 + (i * 0xC), 0) #reset back to original position for writing """So now that we've gotten some info about the original item, we can do some randomization stuff We can randomize a certain group of items depending on where the chest location is now that we have the world and room id of those locations.""" if (checkIfItemExists(ItemList, Item) and not CheckIfChestBlackListed( WorldID, RoomIndex, UniqueChestID)): newChestVal = randomizeAChest(Item, Type, WorldID, RoomIndex, UniqueChestID, EventID) writeIntOrHex(fileBin, newChestVal, 2) newitem = findItemInList(ItemList, newChestVal) if isinstance(newitem, kh2rando_item.KHItem): newItemName = newitem.name else: newItemName = "NoItemFound" newitem = findItemInList(ItemList, Item) if isinstance(newitem, kh2rando_item.KHItem): oldItemName = newitem.name else: oldItemName = "NoItemFound" writeOutCome_Chest(WorldID, RoomIndex, Type, UniqueChestID, oldItemName, newItemName) fileBin.close() else: ErrorWindow( "The file 03system.bin could not be opened. It's probably in use by another program or instance of this program." ) writeRandomizationLog("03system.bin") else: ErrorWindow( "03system.bin does not exist in the export/KH2/ folder. Make sure nothing has gone wrong during the extraction." )
def msnFileCreate( fileName, curWorld, curRoom, oldMsnFileName ): #pass in a mission file name, and the current room and world & will return the new Filename #lets check if the filename is equal to currentworld or currentroom #Lets also check if a mission file ID (last 3 digits) is already present in a room, otherwise we will overwrite something we wouldnt want to and have the player softlock fileExtension = '.bar' oldMsnFileName = oldMsnFileName.rstrip(' \t\r\n\0') if (fileName[:4] == curWorld.upper() + curRoom.upper()): return fileName #We dont need to do anything in this case. searchHeader = oldMsnFileName[:4] newFileName = curWorld.upper() + curRoom.upper() + fileName[ 4:] + '_R' #Add _R so we dont overwrite missions (_R == Randomized) importFolderName = "export/KH2/msn/jp/" if PS3Version(): importFolderName = "export/msn/us/" exportFolderName = "msn/jp/" if PS3Version(): exportFolderName = "msn/us/" if (os.path.isfile(importFolderName + fileName + fileExtension)): if not os.path.exists(exportFolderName): os.makedirs(exportFolderName) try: fileBin = open( importFolderName + oldMsnFileName.rstrip() + fileExtension, 'rb+') except FileNotFoundError: fileBin = open(exportFolderName + oldMsnFileName + fileExtension, 'rb+') barOffset = findBarHeader(fileBin) fileBin.seek(barOffset + 0x18, 0) #Skip to first header and get pos newPos = readAndUnpack(fileBin, 4) fileBin.seek(barOffset + newPos, 0) fileBin.seek(0xD, 1) oldBonusLevel = readAndUnpack(fileBin, 1) fileBin.close() shutil.copyfile(importFolderName + fileName + fileExtension, exportFolderName + newFileName + fileExtension) print("Copying file...") writeRandomizationLog(exportFolderName + newFileName + fileExtension) fileBin = open(exportFolderName + newFileName + fileExtension, 'rb+') #Modify file to use correct bonus level. ModifyExtraFileNames(fileBin, fileName, newFileName) #replaceAllStringInFile(fileBin,fileName.lower(),newFileName.lower()) findHeaderinBAR(fileBin, fileName[:4], True) fileBin.seek(0xD, 1) writeIntOrHex(fileBin, oldBonusLevel, 1) fileBin.close() if PS3Version(): for x in regionFolders: filestring = 'msn/' + x + "/" + newFileName + fileExtension if not os.path.exists('msn/' + x + '/'): os.makedirs('msn/' + x + '/') shutil.copyfile(exportFolderName + newFileName + fileExtension, filestring) print("Copying file for another region...") writeRandomizationLog(filestring) return newFileName else: print("Couldn't find msn file to copy...") return fileName
def randomizeItemDrops(randomItemD, randomItemDP): offSetSeed(7) if (randomItemD or randomItemDP): print("Randomizing item drops from enemies...") if (copyKHFile("00battle.bin")): fileBin = open("00battle.bin", "rb+") if (fileBin): findHeaderinBAR(fileBin, 'przt', True) fileBin.seek(4, 1) #Size 0x18 entryAmt = readAndUnpack(fileBin, 4) entryPos = fileBin.tell() for x in range(entryAmt): fileBin.seek(entryPos + (x * 0x18), 0) entryIndex = readAndUnpack(fileBin, 2) droppedItemList = [0, 0, 0] droppedItemListProbability = [0, 0, 0] fileBin.seek(0xA, 1) writingPosition = fileBin.tell() for i in range(3): #Clear it writeIntOrHex(fileBin, 0, 2) writeIntOrHex(fileBin, 0, 2) #probability == percentage in clear num == 12% == 12 not 0.12 or anything silly #Randomize items if randomItemD: for itemnum in range(len(droppedItemList)): droppedItemList[itemnum] = random.choice( itemDropList).code if randomItemDP: for itemnum in range(len(droppedItemListProbability)): chanceList = [ random.randint(5, 100), random.randint(5, 50), random.randint(5, 25) ] chanceList_Weight = [0.3, 0.5, 0.2] newRando = random.choices( chanceList, k=1, weights=chanceList_Weight)[0] droppedItemListProbability[ itemnum] = newRando # 5% to 100 chance of an item dropping #Write items chanceList = [0, 1, 2, 3] randomItemAmount = random.choices( chanceList, k=1, weights=[0.7, 0.3, 0.2, 0.1])[0] """ fileBin.seek(entryPos + (x * 0x18), 0) fileBin.seek(2,1) orbSelectionList = [] orbListTypes = range(10) for p in range(random.randint(0,3)): #Select types of the enemy will drop orbSelectionList.append(random.choice(orbListTypes)) #Select which orb type to use for e in range(0xA): if e in orbSelectionList: writeIntOrHex(fileBin,random.randint(0,5),1)#Write amount of orbs to drop else: fileBin.seek(1, 1) """ fileBin.seek(writingPosition, 0) for newRandomItems in range(randomItemAmount): writeIntOrHex(fileBin, droppedItemList[newRandomItems], 2) writeIntOrHex( fileBin, droppedItemListProbability[newRandomItems], 2) fileBin.close()
def RandomizeLevelUps(shouldIrandomize): offSetSeed(6) if (shouldIrandomize): print("Modifying level ups for all characters...") if (copyKHFile("00battle.bin")): fileBin = open( "00battle.bin", 'rb+') # skip like 4 hexs because it has useless data lol findHeaderinBAR(fileBin, 'lvup', True) if (fileBin): levelUpsStartPos = fileBin.tell() #gimmie our currentPosition fileBin.seek(0x40, 1) amtOfLevels = readAndUnpack(fileBin, 4) #choose some abilities for some choices LVLLength = len(AbilityList[AbilityTable_enum.LevelUp]) lvlAbilitiesList = [[], [], []] for lvlupability in AbilityList[AbilityTable_enum.LevelUp]: lvlAbilitiesList[0].append( (lvlupability.code, lvlupability.name)) #Sword lvlAbilitiesList[1].append( (lvlupability.code, lvlupability.name)) #Shield lvlAbilitiesList[2].append( (lvlupability.code, lvlupability.name)) #Staff #now shuffle random.shuffle(lvlAbilitiesList[0]) random.shuffle(lvlAbilitiesList[1]) random.shuffle(lvlAbilitiesList[2]) noneString = '' emptyTuple = (0, noneString) AbilityLevelsNums = random.choices( range(1, amtOfLevels), k=LVLLength ) #I'd love to do indiviudal levels based on sword,shield & staff but that would mean weaker lvls for chars in range(13): stats = [ random.randint(0, 5), random.randint(0, 5), random.randint(0, 5), random.randint(0, 5) ] if (chars == 0): #sora stats = [2, 6, 2, 0] if (chars == 1): #donald stats = [1, 5, 2, 5] if (chars == 2): #goofy stats = [5, 0, 2, 4] SwordAbility_Modified = emptyTuple ShieldAbility_Modified = emptyTuple StaffAbility_Modified = emptyTuple for i in range(amtOfLevels): beforeMoving = fileBin.tell() CurrentLevel = i + 1 writeOutCome_LevelUps( chars, i, " STR:" + "{:<4}".format(str(stats[0])) + " MAGIC:" + "{:<4}".format(str(stats[1])) + " DEF:" + "{:<4}".format(str(stats[2])) + " AP:" + "{:<4}".format(str(stats[3])) + " SwordAbility:" + "{:<20}".format(str(SwordAbility_Modified[1])) + " ShieldAbility:" + "{:<20}".format(str(ShieldAbility_Modified[1])) + " StaffAbility:" + "{:<20}".format(str(StaffAbility_Modified[1]))) #EXPNeeded = readAndUnpack(fileBin,4) fileBin.seek(8, 1) #skip exp and stats if (chars == 0): SwordAbility = readAndUnpack(fileBin, 2) ShieldAbility = readAndUnpack(fileBin, 2) StaffAbility = readAndUnpack(fileBin, 2) else: fileBin.seek(0x6, 1) fileBin.seek(beforeMoving, 0) #go back to beginning fileBin.seek(4, 1) #skip exp writeIntOrHex(fileBin, stats[0], 1) #write stats writeIntOrHex(fileBin, stats[1], 1) writeIntOrHex(fileBin, stats[2], 1) writeIntOrHex(fileBin, stats[3], 1) if (chars == 0): writeIntOrHex(fileBin, SwordAbility_Modified[0], 2) writeIntOrHex(fileBin, ShieldAbility_Modified[0], 2) writeIntOrHex(fileBin, StaffAbility_Modified[0], 2) else: fileBin.seek(0x6, 1) #--Increase stats for next time-- #we can only increase 1 or 2 values depending on the ability that is present otherwise the game will not know how to handle it and crash SwordAbility_Modified = emptyTuple ShieldAbility_Modified = emptyTuple StaffAbility_Modified = emptyTuple if (i not in AbilityLevelsNums or chars != 0): statsAmtToRandomize = 2 else: #We will randomize the abilities now SwordAbility_Modified = lvlAbilitiesList[0].pop(0) ShieldAbility_Modified = lvlAbilitiesList[1].pop(0) StaffAbility_Modified = lvlAbilitiesList[2].pop(0) statsAmtToRandomize = 1 randomStat = [0, 1, 2, 3] endingList = [] for x in range(statsAmtToRandomize): randomnum = random.randint(0, len(randomStat) - 1) poppednum = randomStat.pop(randomnum) endingList.append(poppednum) for y in endingList: stats[y] += random.randint(0, 3) #STR = readAndUnpack(fileBin,1) #MAG = readAndUnpack(fileBin,1) #DEF = readAndUnpack(fileBin,1) #AP = readAndUnpack(fileBin,1) fileBin.seek(2, 1) # skip two bytes fileBin.seek(0x4, 1) # skip four bytes fileBin.close()
def RandomizeBonusLevels(bonusLevelBool, bonusItemBool, critBonusBool, extraAbilitiesInt, GiveGuard): offSetSeed(3) def getBonusLevelsFromMSN(): fileBonuslevels = [] path = "export/KH2/msn/jp/" if PS3Version(): path = "export/msn/us/" msnFileList = getAllFileNamesInFolder(path) for msnFile in msnFileList: fileBin = open(path + msnFile, 'rb+') barOffset = findBarHeader(fileBin) fileBin.seek(0x18 + barOffset, 0) # Skip to first header and get pos newPos = readAndUnpack(fileBin, 4) fileBin.seek(newPos + barOffset, 0) fileBin.seek(0xD, 1) fileBonuslevels.append(readAndUnpack(fileBin, 1)) fileBin.close() fileBonuslevels = sorted(list(dict.fromkeys(fileBonuslevels))) return fileBonuslevels if GiveGuard: newList = list( filter(lambda d: d.code == 0x052, AbilityList[ AbilityTable_enum.Action])) # Check for same character newList[0].maximum = 0 if extraAbilitiesInt == 2: #If user asks for more abilities newList = list( filter(lambda d: d.character == KHCharacter.Sora, AbilityList[ AbilityTable_enum.Support])) # Check for same character newList = list( filter(lambda d: d.abilitytype == AbilityType.Additive, newList)) #Grab from level up pool too! newList2 = list( filter(lambda d: d.character == KHCharacter.Sora, AbilityList[ AbilityTable_enum.LevelUp])) # Check for same character newList2 = list( filter(lambda d: d.abilitytype == AbilityType.Additive, newList2)) newList = newList + newList2 for i in range(20): #Give like 20 more bonus abilities itemToCopy = random.choices(newList, k=1)[0] itemCopied = copy.deepcopy(itemToCopy) itemCopied.abilityLearned = AbilityTypeGained.Normal AbilityList[AbilityTable_enum.Support].append(itemCopied) if critBonusBool: # Randomize critical mode abilities by setting them to normal and grabbing random normal abilities newList = list( filter(lambda d: d.character == KHCharacter.Sora, AbilityList[ AbilityTable_enum.Support])) # Check for same character newList = list( filter( lambda d: d.abilityLearned == AbilityTypeGained.CriticalBonus, newList)) newList = list(filter(lambda d: d.emptyItem(), newList)) critAbilitiesNum = 0 for abilityitem in newList: critAbilitiesNum += abilityitem.maximum abilityitem.abilityLearned = AbilityTypeGained.Normal amountOfAbilitiesNum = 0 while amountOfAbilitiesNum < critAbilitiesNum: newList = list( filter(lambda d: d.character == KHCharacter.Sora, AbilityList[AbilityTable_enum.Support])) newList = list( filter(lambda d: d.abilityLearned == AbilityTypeGained.Normal, newList)) newList = list(filter(lambda d: d.emptyItem(), newList)) itemsToBonus = random.choice(newList) index = AbilityList[AbilityTable_enum.Support].index(itemsToBonus) itemsToBonus = AbilityList[AbilityTable_enum.Support][index] if itemsToBonus.abilityLearned == AbilityTypeGained.Normal: if itemsToBonus.maximum + amountOfAbilitiesNum <= critAbilitiesNum: amountOfAbilitiesNum += itemsToBonus.maximum itemsToBonus.abilityLearned = AbilityTypeGained.CriticalBonus #character,type of boost CharBoosts = [] bonusLevelCharData = [] for i in range(15): bonusLevelCharData.append([]) abilityChoosing = [] for i in range(15): abilityChoosing.append([]) abilitesForEachChar = [] for i in range(3): abilitesForEachChar.append(0) #get number of bonus abilities for each char for char in KHCharacter: amount = 0 for type in range(len(AbilityList)): if type != AbilityTable_enum.LevelUp: for itemAb in AbilityList[type]: if itemAb.character == char and itemAb.abilityLearned == AbilityTypeGained.Normal and itemAb.emptyItem( ): amount += 1 abilitesForEachChar[char] = amount for char in range(3): CharBoosts.append([0]) for type in range(6): CharBoosts[char].append(0) #HP MP ARMOR ACCESSORY ITEM DRIVE maxCharBoosts = [[20, 4, 3, 6, 5, 3], [18, 0, 1, 0, 2, 1], [17, 0, 1, 0, 3, 1]] def randomizeABonusLevel(hp, mp, armor, access, item, drive, abilityitem1, abilityitem2, char, bonslevel): nonlocal GiveGuard oldChar = char if char + 1 == 0xE: # If the character is roxas, change it to sora's num because theyre the same char = 0 hp = mp = armor = drive = item = access = abilityitem2 = abilityitem1 = 0 #Reset all currentNotifications = 0 typeOfAbility = -1 abilityitem1_name = "" abilityitem2_name = "" if bonslevel == 0x36: currentNotifications = 2 #Do not randomize Stats if the bonus level is 36/ Aerial recovery since it doesnt give that for some reason if GiveGuard: abilityitem1 = 0x052 abilityitem1_name = "Guard (Forced)" if (BonusLevel not in bonusNumList ): # Blacklist bonus levels not in the game. currentNotifications = 2 # #handle ability swapping here if bonslevel in abilityChoosing[ char]: #if the level we are is the same level that was randomly chosen to get an ability randomize by choosing between two ability types def filterListQuick(AbilityTp): newList = list( filter(lambda d: d.character == char, AbilityList[AbilityTp])) # Check for same character newList = list( filter( lambda d: d.abilityLearned == AbilityTypeGained.Normal, newList)) # Normal abilities only newList = list(filter(lambda d: d.emptyItem(), newList)) # Check if empty ability return newList abilityChoosing[char].remove(bonslevel) possibleChoices = [] for types in range(2): if len(filterListQuick(types)) != 0: possibleChoices.append(types) if len(possibleChoices) != 0: typeOfAbility = random.choice(possibleChoices) newList = filterListQuick(typeOfAbility) random.shuffle(newList) abilityitem1 = newList[0] index = AbilityList[typeOfAbility].index(abilityitem1) AbilityList[typeOfAbility][index].subtractQOne( ) #subtract cause we're using it abilityitem1 = AbilityList[typeOfAbility][index].code abilityitem1_name = AbilityList[typeOfAbility][index].name currentNotifications += 1 usedThisRound = [0, 0, 0, 0, 0, 0] while currentNotifications < 2: #Do stat boosts here randomizeBoostList = [] for x in CharacterBoostType: if (CharBoosts[char][x] < maxCharBoosts[char][x] and not usedThisRound[x]): randomizeBoostList.append(x) if len(randomizeBoostList) != 0: slotToRandomize = random.choices(randomizeBoostList, k=1)[0] #handle adding to new boosts for boosttype in CharacterBoostType: if (slotToRandomize == boosttype): if boosttype == CharacterBoostType.HPBoosts: hp = KHCharacterHPTable[char] if boosttype == CharacterBoostType.MPBoosts: mp = 0xa if boosttype == CharacterBoostType.DriveUpgrades: drive = 1 if boosttype == CharacterBoostType.ArmorSlots: armor = 1 if boosttype == CharacterBoostType.Items: item = 1 if boosttype == CharacterBoostType.Accessory: access = 1 usedThisRound[boosttype] = True CharBoosts[char][boosttype] += 1 currentNotifications += 1 else: break if bonusItemBool and currentNotifications < 2: # After everything is done and we still have an empty slot, give an bonus item tablesToChooseFrom = (kh2rando_itemTable.accessory_table, kh2rando_itemTable.synthesis_table, kh2rando_itemTable.item_table) weightTable = (0.5, 0.4, 0.8) choosenChoice = random.choices(tablesToChooseFrom, weights=weightTable, k=1)[0] choosenItemKey = random.choice(list(choosenChoice.keys())) choosenItem = choosenChoice[choosenItemKey] abilityitem2 = choosenItem[3] abilityitem2_name = choosenItemKey currentNotifications += 1 if hp == 0 and mp == 0 and armor == 0 and drive == 0 and item == 0 and access == 0 and abilityitem1 == 0 and abilityitem2 == 0: alertDebug = 0 if (BonusLevel in bonusNumList): writeOutCome_BonusLevel( oldChar, bonslevel, "HP:" + "{:<4}".format(str(hp)) + " MP:" + "{:<4}".format(str(mp)) + " ARMOR:" + "{:<4}".format(str(armor)) + " DRIVE:" + "{:<4}".format(str(drive)) + " ITEM:" + "{:<4}".format(str(item)) + " ACCESSORY:" + "{:<4}".format(str(access)) + " Ability/Item:" + ("{:<20}".format(abilityitem1_name)) + " Ability/Item 2:" + ("{:<20}".format(abilityitem2_name))) #Returning format might be wrong!!!! #hp mp drive?,item?,armor,access return (hp, mp, drive, item, armor, access, abilityitem1, abilityitem2) if (bonusLevelBool): print("Modifying bonus levels...") bonusNumList = getBonusLevelsFromMSN() if (copyKHFile("00battle.bin")): fileBin = open( "00battle.bin", 'rb+') # skip like 4 hexs because it has useless data lol findHeaderinBAR(fileBin, 'bons', True) if (fileBin): levelBonusStartPos = fileBin.tell( ) #gimmie our currentPosition fileBin.seek(4, 1) amtOfBonusLevels = readAndUnpack(fileBin, 4) # for i in range( amtOfBonusLevels + 1 ): #Get the amount of bonus levels for specific characters fileBin.seek( levelBonusStartPos + 8 + (i * 0x10), 0 ), # reset back to original position for writing thats coming up (Plus two to skip bonus level and character) BonusLevel = readAndUnpack(fileBin, 1) # Bonus level Character = readAndUnpack( fileBin, 1 ) # Character value. 01 == sora 02 == donald 03 == goofy ...etc for others if Character == 0xE: # If the character is roxas, change it to sora's num because theyre the same Character = 1 if (BonusLevel != 0x36 or not GiveGuard): if ( BonusLevel in bonusNumList ): #Make sure the bonus level is actually used ingame. bonusLevelCharData[Character - 1].append(BonusLevel) #now that we have data, choose ability locations throughout the bonuses for char in KHCharacter: for choicenum in range(abilitesForEachChar[char]): newChoice = random.choice(bonusLevelCharData[char]) popout = bonusLevelCharData[char].pop( bonusLevelCharData[char].index(newChoice)) abilityChoosing[char].append(popout) randomBonusLevelLoop = list(range(amtOfBonusLevels + 1)) random.shuffle( randomBonusLevelLoop ) # We will randomize the order we go through chests in in order to be a bit more random. Kinda odd but yea so we evenly spread out priority items instead of like in a row. for i in randomBonusLevelLoop: fileBin.seek( levelBonusStartPos + 8 + (i * 0x10), 0 ), # reset back to original position for writing thats coming up (Plus two to skip bonus level and character) BonusLevel = readAndUnpack(fileBin, 1) # Bonus level Character = readAndUnpack( fileBin, 1 ) # Character value. 01 == sora 02 == donald 03 == goofy ...etc for others HPVal = readAndUnpack(fileBin, 1) # HP Value. MPVal = readAndUnpack(fileBin, 1) # MP Value. SlotUpgrades_Armor = readAndUnpack( fileBin, 1) # Slot upgrade to player or party member SlotUpgrades_Accessory = readAndUnpack( fileBin, 1) # Slot upgrades to player or party member SlotUpgrades_Item = readAndUnpack( fileBin, 1) # Slot upgrades to player or party member SlotUpgrades_Drive = readAndUnpack( fileBin, 1) # Slot upgrades to player or party member AbilityItem1 = readAndUnpack( fileBin, 2) # Ability upgrades to player or party member AbilityItem2 = readAndUnpack( fileBin, 2) # Ability upgrades to player or party member fileBin.seek(levelBonusStartPos + 8 + 2 + (i * 0x10), 0) if (Character >= 1 and Character < 4 or Character == 0xE): newbonusLevelVal = randomizeABonusLevel( HPVal, MPVal, SlotUpgrades_Armor, SlotUpgrades_Drive, SlotUpgrades_Item, SlotUpgrades_Accessory, AbilityItem1, AbilityItem2, Character - 1, BonusLevel) for y in range(6): writeIntOrHex(fileBin, newbonusLevelVal[y], 1) #write hp,mp,slot,slot,slot,slot writeIntOrHex(fileBin, newbonusLevelVal[6], 2) # write ability writeIntOrHex(fileBin, newbonusLevelVal[7], 2) # write ability fileBin.close()
def randomizeMusic(ranMusic): if ranMusic: offSetSeed(50) print("Modifying music...") path = "export/KH2/ard" if PS3Version(): path = "export/ard/us" fileList = getAllFileNamesInFolder(path) for j in fileList: musicOccurenceNum = 0 musicListUsed= [] filename = j currentWorld = filename[0:2].upper() currentRoom = int(filename[2:4]) # shutil.copyfile(path+ "/" +filename, "ard/"+filename) #Move to native folder regionExtraFolder = "" if PS3Version(): regionExtraFolder = "us/" filename = "ard/"+regionExtraFolder + filename if copyKHFile(filename): writeRandomizationLog(filename) fileBin = open(filename, 'rb+') """ Notes: Affecting music data also affects the space of ram. Great. Some places may softlock because of this..... Another music weighting system based on file size? EVT Data: Amt of entries not given. Size of entries not given. Anything after the evt bar entry in the main header will be in evt, btl header sometimes Start of EVT entry depending on event type: Event ID, Used by a general event. EX: Bosses with their respective cutscene on death/enter Empty? Event 2ndID, Possibly a type? Changes and isnt very consistent. Event 2ndID's when a boss is assigned to an eventId may be: 2C if 48 (Super Boss?) 22 if 44 (Normal Boss?) possibly use an if statement if an value is above an amount Music event starts with 10 00 01? Wow, what a waste of time trying to decipher this 5 Unknown Bytes, Music Num Data, 0C == Current World music? 09 == Current World Battle Music? Empty, Music Num Data, FF Sometimes ends the data, sometimes it doesn't. """ if findHeaderinBAR(fileBin, 'evt', False): fileBin.seek(0x8, 1) evtSize = readAndUnpack(fileBin, 4) """ Useless eventIDList = [] if findHeaderinBAR(fileBin, 'map', False): fileBin.seek(0x8, 1) mapSize = readAndUnpack(fileBin, 4) findHeaderinBAR(fileBin, 'map', True) # go back and start going through filePosition = fileBin.tell() mapFileText = fileBin.read(mapSize) fileBin.seek(filePosition, 0) difBytes = [b'\x01\x00\x02'] for differentEvtTypes in difBytes: curPos = 0 while (mapFileText.count(differentEvtTypes, curPos) != 0): curPos = mapFileText.find(differentEvtTypes, curPos) # Skip 5 bytes fileBin.seek(filePosition + curPos, 0) fileBin.seek(-0x4, 1) # Go back, find 2nd id eventIDList.append(readUnpackGoBack(fileBin, 1)) curPos += 1 if len(eventIDList) != 0: debughi = 0 """ findHeaderinBAR(fileBin, 'evt', True) # go back and start going through filePosition = fileBin.tell() evtFileText = fileBin.read(evtSize) fileBin.seek(filePosition, 0) difBytes = [b'\x10\x00\x01'] for differentEvtTypes in difBytes: curPos = 0 while (evtFileText.count(differentEvtTypes, curPos) != 0): curPos = evtFileText.find(differentEvtTypes, curPos) # Skip 5 bytes fileBin.seek(filePosition + curPos, 0) if not CheckIfMusicBlackList(currentWorld,currentRoom,musicOccurenceNum): fileBin.seek(-0x4, 1) # Go back, find 2nd id evtFirstEventID = readAndUnpack(fileBin, 1) fileBin.seek(1, 1) evtSecondEventID = readAndUnpack(fileBin, 1) fileBin.seek(1, 1) fileBin.seek(len(differentEvtTypes), 1) # skip over what we just found fileBin.seek(0x1, 1) # skip empty firstMusicID = readUnpackGoBack(fileBin, 1) random.shuffle(musicList) chooseNewMusic = musicList[0] for xd in musicListUsed: if evtFirstEventID == xd[0]: chooseNewMusic =xd[1] writeIntOrHex(fileBin, chooseNewMusic, 1) fileBin.seek(0x1, 1) # skip empty firstMusicID2 = readUnpackGoBack(fileBin, 1) writeIntOrHex(fileBin, chooseNewMusic, 1) usedOnce = False for xd in musicListUsed: if evtFirstEventID == xd[0]: usedOnce = True if not usedOnce: musicListUsed.append((evtFirstEventID,chooseNewMusic)) curPos += 1 musicOccurenceNum+=1 fileBin.close()
def enemyBTLMSNEdit(): #PS3 Version Note: Little Endian to Big Endian change: every 2 bytes is swapped. GREAT. #Bytearray= "BB"[::-1] + "50"[::-1] + "M_"[::-1] + "1S"[::-1] + "40"[::-1] +".B"[::-1] """ def replaceAllMSN(): #very destructive, has problems with ard files that has multiple bosses nonlocal x # write new mission file BeforeReplaced = fileBin.read(18) # i assume 18 is the most????? fileBin.seek(0, 0) replace1 = BeforeReplaced.decode('utf-8').rstrip('\x00') strLength = len(bossMSNTable[x]) strLength2 = len(replace1) spacingNum = strLength2 - strLength beforeEncode = bossMSNTable[x] beforeEncode = msnFileCreate(beforeEncode, currentWorld, currentRoom) for x in range(spacingNum): beforeEncode += '\x00' for x in range(-spacingNum): replace1 += '\x00' replace2 = str.encode(beforeEncode) replace1 = str.encode(replace1) # note: we should add extra bytes based on length so we dont offset the file if we replace stuff allData = fileBin.read() allData = allData.replace(replace1, replace2) # oh my god i forget to store it back and i was wondering why it wasnt working fileBin.seek(0, 0) fileBin.write(allData) """ def replaceOneMSN(newMSN): # write new mission file BeforeReplaced = fileBin.read(0x20) # i assume 18 is the most????? BeforeReplaced = BeforeReplaced.decode('utf-8').rstrip('\x00') BeforeReplaced = ReverseEndianString(BeforeReplaced,2,True) fileBin.seek(-0x20, 1) beforeEncode = msnFileCreate(newMSN,currentWorld,currentRoom,BeforeReplaced) """ All of this doesn't work when trying to fix Final Xemnas MSN softlocking. Great. exportFolderName = "msn/jp/" if PS3Version(): exportFolderName = "msn/us/" fileBin_MSN = open(exportFolderName + beforeEncode+'.bar','rb+') findHeaderinBAR(fileBin_MSN,newMSN[:4],True) fileBin_MSN.write(bytearray([0x00, 0x02, 0x01 ,0x11 ,0x11 ,0x71])) findHeaderinBAR(fileBin_MSN, newMSN[:4], False) fileBin_MSN.seek(0x4+0x4,1) barSize = readAndUnpack(fileBin_MSN,4) findHeaderinBAR(fileBin_MSN, newMSN[:4], True) fileBin_MSN.seek(barSize,1) beforePos = fileBin_MSN.tell() findHeaderinBAR(fileBin_MSN,newMSN[:4],False) fileBin_MSN.seek(0x10 + 0x4,1) #Skip down an entry and move to New location newPos = readAndUnpack(fileBin_MSN,4) offsetPos = findBarHeader(fileBin_MSN) fileBin_MSN.seek(newPos+offsetPos,0) writingString = 'eh20_ms113'.ljust(0x10,'\x00').encode() #Write new mission objective fileBin_MSN.write(writingString) fileBin_MSN.close() appendToSpecificPos(exportFolderName + beforeEncode+'.bar',beforePos,newMSN[:4],bytearray([0x08, 0xD1, 0x00, 0x04 ,0x00 ,0x00, 0x00, 0x00])) """ beforeEncode = ReverseEndianString(beforeEncode, 2, True,True) spacingNum = 0x20 - len(beforeEncode) # note: we should add extra bytes based on length so we dont offset the file if we replace stuff for x in range(spacingNum): beforeEncode += '\x00' replace2 = str.encode(beforeEncode) fileBin.write(replace2) fileBin.seek(-len(replace2), 1) #Write, go back and we're done. #eh20_ms113 == replace ms_boss if the final boss mission is being replaced EH20_MS113.bar #end of utility functions. start of main function #This maybe need to be looked at again. btl_String = 'btl' findHeaderinBAR(fileBin,btl_String,True) # find "btl" header thing # go to position from the btl header endcodedCheck = ReverseEndianString(check,2,True).encode('utf-8') # get entry name ex: b_00 posBeforeRead = fileBin.tell() data2 = fileBin.read() seekToRead = data2.find(endcodedCheck) fileBin.seek(posBeforeRead, 0) fileBin.seek(seekToRead, 1) # goto name closest # find original msn by backing up a lil so we find the currentworld string posBeforeRead = fileBin.tell() seekToRead = -1 offset = 0 #15 00 09 find this byte and offset foward by 4 bytes and write this way while seekToRead != 0: # This is slow..... But the best I can do ATM. fileBin.seek(posBeforeRead, 0) offset += 1 if offset > 300: return #We probrably can't find anything because an boss has already taken its place. In this case, we will just stop the function fileBin.seek(fileBin.tell() - offset, 0) data3 = fileBin.read() findThing =data3.find(b'\x15\x00\x09') if findThing == 0: seekToRead = findThing+3+4 #Skip the byte we found and the following 4 bytes to get to the string break; fileBin.seek(seekToRead, 1) fileBin.seek(posBeforeRead, 0) fileBin.seek(-offset, 1) offset = 0 if not PS3Version(): offset = 1 fileBin.seek(seekToRead+offset, 1) testRead = fileBin.read(0x20) testRead = ReverseEndianString(testRead.decode('utf-8'),2,True).rstrip('\x00') fileBin.seek(-0x20, 1) if not testRead == 'EH20_MS113': # Final Xemnas Specific dont edit if testRead== "HB38_FM_MAR" or testRead == 'CA01_MS204': #Replace Marluxia MSN as it softlocks at the start of battle and Grim Reaper Mission cause it crashes on PS3 replaceOneMSN("HB33_FM_LAR") #Generic Battle MSN file enemyBTLMSNEdit() # Try again...... else: for x in bossMSNTable: if enemyToWrite.code == x: replaceOneMSN(bossMSNTable[x])
def randomizeEquipmentStats(randomEquipStat, randomEquipAbility): offSetSeed(8) if (randomEquipStat or randomEquipAbility): print("Randomizing equipment stats...") if (copyKHFile("03system.bin")): fileBin = open("03system.bin", "rb+") if (fileBin): findHeaderinBAR(fileBin, 'item', True) #Size 0x18 fileBin.seek(4, 1) #Skip ?? entryAmt = readAndUnpack(fileBin, 4) entryPos = fileBin.tell() StatusEffectIdList = [] for x in range(entryAmt): fileBin.seek(entryPos + (x * 0x18), 0) ItemID = readAndUnpack(fileBin, 2) CategoryID = readAndUnpack( fileBin, 1 ) #This is really conflicting. Sometimes the status ID is only 2 bytes away and sometimes its 4. :< if CategoryID == 0x0E or CategoryID == 0x0F: #If armor or accessory fileBin.seek(1, 1) elif CategoryID == 0x02 or CategoryID == 0x03 or CategoryID == 0x04: #If keyblade/shield/staff fileBin.seek(3, 1) else: continue #Skip, we dont want any other item if it isnt an armor, accessory or keyblade/weapon StatusID = readAndUnpack(fileBin, 2) StatusEffectIdList.append((StatusID, ItemID)) RealStatusItems = [ ] #if the item matches up with an equipment, accessory or armor item we will add it to the list along with its object for later reference for itemThing in StatusEffectIdList: for itemReal in newItemList: if itemReal.code == itemThing[1] and itemThing[0] != 0: RealStatusItems.append((itemThing[0], itemReal)) fileBin.seek(entryPos + (entryAmt * 0x18), 0) statusModifier = [] for whatever in range(12): statusModifier.append(0) fileBin.seek(4, 1) statusAmt = readAndUnpack(fileBin, 4) #I counted statusPos = fileBin.tell() for x in range(statusAmt): statusPos_Entry = fileBin.tell() statsID = readAndUnpack(fileBin, 2) abilityID = readAndUnpack(fileBin, 2) for d in range(12): statusModifier[d] = readAndUnpack(fileBin, 1) #Modify ifinStatusItem = False itemToUse = None for xd in RealStatusItems: if xd[0] == statsID: itemToUse = xd[1] ifinStatusItem = True break if ifinStatusItem: fileBin.seek(statusPos_Entry + 2, 0) if randomEquipAbility: if itemToUse.type == itemType.Equipment or ( itemToUse.type == itemType.Accessory and random.randint(0, 5) > 4 ): #If the player is lucky, an ability is added. abilityID = random.choice(newAbilityList).code if randomEquipStat: if itemToUse.type == itemType.Equipment or itemToUse.type == itemType.Accessory: chanceList = [ random.randint(1, 6), random.randint(1, 10), random.randint(0, 10) ] chanceList_Weight = [0.3, 0.5, 0.2] newRando = random.choices( chanceList, k=1, weights=chanceList_Weight)[0] chanceList = [ random.randint(1, 6), random.randint(1, 10), random.randint(0, 10) ] newRando2 = random.choices( chanceList, k=1, weights=chanceList_Weight)[0] statusModifier[ EquipmentStats.StrengthStat] = newRando statusModifier[ EquipmentStats.MagicStat] = newRando2 if itemToUse.type == itemType.Accessory: chanceList = [ random.randint(1, 30), random.randint(1, 10), random.randint(0, 10) ] chanceList_Weight = [0.3, 0.5, 0.2] newRando = random.choices( chanceList, k=1, weights=chanceList_Weight)[0] statusModifier[ EquipmentStats.APStat] = newRando if itemToUse.type == itemType.Armor: chanceList = [ random.randint(1, 5), random.randint(1, 4), random.randint(0, 10) ] chanceList_Weight = [0.3, 0.5, 0.2] newRando = random.choices( chanceList, k=1, weights=chanceList_Weight)[0] statusModifier[ EquipmentStats.DefenseStat] = newRando for resistNums in range(4, 9): statusModifier[resistNums] = random.randint( 75, 150 ) #Not too OP, but gives the chance of reducing resistance #for resistNums in range(9,10): #Global resist and invisible stuff so dont go too crazy #statusModifier[resistNums] = random.randint(80,120) writeIntOrHex(fileBin, abilityID, 2) for d in range(12): writeIntOrHex(fileBin, statusModifier[d], 1)