Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #Lets Randomize Relics
- #Made by setz
- #@splixel on twitter
- #twitch.tv/skiffain
- #too lazy for licenses, pretend I attached WTFPL
- #(do what the fuck you want with this)
- #error_recalc comes from https://www.romhacking.net/utilities/1264/
- #Conditions for flight are one of the following
- #Soul of Bat (ez mode)
- #Gravity Boots + Leap Stone (chaining gravity jumps)
- #Form of Mist + Power of Mist (fly as mist)
- #Requirements for accessing castle 2 are
- #Flight
- #Jewel of Open
- #Mist
- import random
- from subprocess import call
- from binascii import hexlify
- from datetime import datetime
- FileName = "Castlevania - Symphony of the Night (USA) (Track 1).bin"
- #RandomizeBossRelics = False #Not Supported
- ShowItemPlacements = True #Print out a log of when items are placed
- #RandoSeed = 1234567890
- RandoSeed = datetime.time(datetime.now())
- #Ability Checks
- HasLeapStone = False
- HasGravityBoots = False
- HasJewelOfOpen = False
- HasMist = False
- HasPowerOfMist = False
- HasBat = False
- HasWolf = False
- HasSonar = False
- HasMermanStatue = False
- #TODO
- #accept args
- #args: Player Seed input
- #args for other options
- #Options to not randomize boss relics
- #Copy files instead of just overwriting
- #do a hash check to ensure its editing the right file
- #possibly make a small frontend for it?
- #Known Bugs
- #need to trace relics from left/right as well as up/down because of how sotn loads entities
- #List of known delinquents
- #Cube Of Zoe
- #Spirit Orb
- #Farie Scroll
- #Leap Stone
- #Some relics have doubles, so..
- #Relic ID/Name #RelicLocation ID #Cant Be Behind RL ID No-Gos
- #00 Soul of Bat 00
- #01 Fire of Bat 01
- #02 Echo of Bat 02 Castle 2 18 19 1a 1b 1c
- #03 Force of Echo 03
- #04 Soul of Wolf 04
- #05 Power of Wolf 05
- #05 Power of Wolf 05
- #06 Skill of Wolf 06
- #07 Form of Mist 07 Castle 2, Mist Gates 18 19 1a 1b 1c 00
- #08 Power of Mist 08
- #09 Gas Cloud 09
- #0A Cube of Zoe 0a
- #0A Cube of Zoe 0a
- #0B Spirit Orb 0b
- #0C Gravity Boots 0c
- #0D Leap Stone 0d
- #0E Holy Symbol 0e
- #0F Faerie Scroll 0f
- #10 Jewel of Open 10 Castle 2, Jewel Doors 18 19 1a 1b 1c 0d 0e 11 15
- #11 Merman Statue 11 Holy Snorkel Location 0e
- #12 Bat Card 12
- #13 Ghost Card 13
- #14 Faerie Card 14
- #15 Demon Card 15
- #16 Sword Card 16
- #17 Sprite Card --
- #18 Nosedevil Card --
- #19 Heart of Vlad 17
- #19 Heart of Vlad 17
- #1A Tooth of Vlad 18
- #1A Tooth of Vlad 18
- #1B Rib of Vlad 19
- #1B Rib of Vlad 19
- #1C Ring of Vlad 1a
- #1C Ring of Vlad 1a
- #1D Eye of Vlad 1b
- #1D Eye of Vlad 1b
- RelicLocation = [0x047a5b66, 0x0557535e, 0x04aa4156, 0x0526e6a8, 0x049d6596, 0x04b6b9b4, 0x054b1d5a, 0x043c578a,
- 0x05610db8, 0x04cfcb16, 0x04b6b946, 0x048fd1fe, 0x048fc9ba, 0x05610dc2, 0x04c34ee6, 0x047a5720,
- 0x047a321c, 0x04c35174, 0x054b1d58, 0x05611958, 0x047a5784, 0x045ea95e, 0x04aa3f76, 0x06306ab2,
- 0x05051d52, 0x069d2b1e, 0x059bdb30, 0x04da65f2]
- DoubleLocation = [0, 0, 0, 0, 0, 0x053f971c, 0, 0,
- 0x0561142C, 0, 0x053F969A, 0, 0, 0, 0, 0,
- 0, 0, 0, 0x0561127c, 0, 0, 0, 0x04e335b4,
- 0x067d1630, 0x050fa914, 0x059ee2e4, 0x0662263a]
- TripleLocation = [0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x04b6b08a, 0x048fe280, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0]
- QuadrupleLocation = [0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0x053f8e2e, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0]
- RelicList = []
- RelicList = [bytearray([0x00]),
- bytearray([0x01]),
- bytearray([0x02]),
- bytearray([0x03]),
- bytearray([0x04]),
- bytearray([0x05]),
- bytearray([0x06]),
- bytearray([0x07]),
- bytearray([0x08]),
- bytearray([0x09]),
- bytearray([0x0a]),
- bytearray([0x0b]),
- bytearray([0x0c]),
- bytearray([0x0d]),
- bytearray([0x0e]),
- bytearray([0x0f]),
- bytearray([0x10]),
- bytearray([0x11]),
- bytearray([0x12]),
- bytearray([0x13]),
- bytearray([0x14]),
- bytearray([0x15]),
- bytearray([0x16]),
- bytearray([0x19]),
- bytearray([0x1a]),
- bytearray([0x1b]),
- bytearray([0x1c]),
- bytearray([0x1d])]
- RelicsUsed = []
- LocationsUsed = []
- for i in range(0, len(RelicList)):
- RelicsUsed.append(False)
- LocationsUsed.append(False)
- def ReplaceByte(ByteLocation, NewByte):
- with file(FileName, "r+b") as HackThisRom:
- HackThisRom.seek(ByteLocation)
- HackThisRom.write(NewByte)
- return True
- def PlaceItem(Item, Location):
- if ShowItemPlacements:
- print("Placing Item: "+hexlify(RelicList[Item]))
- #print("Location a: "+str(hex(RelicLocation[Location])))
- ReplaceByte(RelicLocation[Location], RelicList[Item])
- if (DoubleLocation[Location] != 0):
- #if ShowItemPlacements:
- # print("Location b: "+str(hex(DoubleLocation[Location])))
- ReplaceByte(DoubleLocation[Location], RelicList[Item])
- if (TripleLocation[Location] != 0):
- #if ShowItemPlacements:
- # print("Location b: "+str(hex(DoubleLocation[Location])))
- ReplaceByte(TripleLocation[Location], RelicList[Item])
- if (QuadrupleLocation[Location] != 0):
- #if ShowItemPlacements:
- # print("Location b: "+str(hex(DoubleLocation[Location])))
- yReplaceByte(QuadrupleLocation[Location], RelicList[Item])
- #Check abilities if possible
- global HasJewelOfOpen
- global HasLeapStone
- global HasMist
- global HasPowerOfMist
- global HasGravityBoots
- global HasBat
- global HasWolf
- global HasSonar
- global HasMermanStatue
- if Item == 0x10:
- HasJewelOfOpen = True
- elif Item == 0xd:
- HasLeapStone = True
- elif Item == 0x7:
- HasMist = True
- elif Item == 0x8:
- HasPowerOfMist = True
- elif Item == 0xc:
- HasGravityBoots = True
- elif Item == 0x4:
- HasWolf = True
- elif Item == 0x0:
- HasBat = True
- elif Item == 0x2:
- HasSonar = True
- elif Item == 0x11:
- HasMermanStatue = True
- #Mark as used
- RelicsUsed[Item] = True
- LocationsUsed[Location] = True
- return True
- def FindUnplacedRelic():
- #print(len(RelicList))
- RandIndex = random.randint(0, len(RelicList)-1)
- if RelicsUsed[RandIndex]:
- return FindUnplacedRelic()
- else:
- return RandIndex
- def FindUnplacedLocation(InputArray):
- RandIndex = InputArray[random.randint(0, len(InputArray)-1)]
- if LocationsUsed[RandIndex]:
- return FindUnplacedLocation(InputArray)
- else:
- return RandIndex
- def SoftUnlock():
- #print(str(HasJewelOfOpen)+" | "+str(HasLeapStone)+" | "+str(HasMist)+" | "+str(HasPowerOfMist)+" | "+str(HasGravityBoots)+" | "+str(HasBat)+" | "+str(HasSonar)+" | "+str(HasMermanStatue))
- #List of available locations
- LocationsAvailable = []
- #Starting Areas
- if LocationsUsed[0x04] == False:
- LocationsAvailable.append(0x04)
- if LocationsUsed[0x0a] == False:
- LocationsAvailable.append(0x0a)
- if LocationsUsed[0x0b] == False:
- LocationsAvailable.append(0x0b)
- if LocationsUsed[0x0f] == False:
- LocationsAvailable.append(0x0f)
- if LocationsUsed[0x10] == False:
- LocationsAvailable.append(0x10)
- #Restricted Areas
- if HasMist and (HasLeapStone or HasGravityBoots or HasBat):
- #Soul of Bat Vanilla
- if LocationsUsed[0x00] == False:
- LocationsAvailable.append(0x00)
- if HasBat or (HasGravityBoots and HasLeapStone) or (HasMist and HasPowerOfMist):
- #Flight only
- if LocationsUsed[0x01] == False:
- LocationsAvailable.append(0x01)
- if LocationsUsed[0x05] == False:
- LocationsAvailable.append(0x05)
- if LocationsUsed[0x08] == False:
- LocationsAvailable.append(0x08)
- if LocationsUsed[0x0c] == False:
- LocationsAvailable.append(0x0c)
- if LocationsUsed[0x13] == False:
- LocationsAvailable.append(0x13)
- if LocationsUsed[0x16] == False:
- LocationsAvailable.append(0x16)
- if (HasBat or (HasMist and HasPowerOfMist) or (HasGravityBoots and HasLeapStone)) and (HasMist or HasWolf or HasBat):
- if LocationsUsed[0x02] == False: #Olrox's Prize
- LocationsAvailable.append(0x02)
- if HasGravityBoots or HasBat or (HasMist and HasPowerOfMist):
- #Gravity Boots or better
- if LocationsUsed[0x06] == False:
- LocationsAvailable.append(0x06)
- if LocationsUsed[0x12] == False:
- LocationsAvailable.append(0x12)
- if LocationsUsed[0x14] == False:
- LocationsAvailable.append(0x14)
- if HasLeapStone or HasGravityBoots or HasBat or (HasMist and HasPowerOfMist):
- #Leapstone or better
- if LocationsUsed[0x07] == False:
- LocationsAvailable.append(0x07)
- if LocationsUsed[0x0d] == False:
- LocationsAvailable.append(0x0d) #Colosseum - only required if leap stone?
- if HasJewelOfOpen:
- #Bottom of the castle
- if LocationsUsed[0x11] == False:
- LocationsAvailable.append(0x11)
- if HasJewelOfOpen and HasLeapStone or HasBat or (HasMist and HasPowerOfMist):
- if LocationsUsed[0x15] == False: #Demon card a bitch
- LocationsAvailable.append(0x15)
- if HasMermanStatue and HasJewelOfOpen:
- #holy snorkel vanilla
- if LocationsUsed[0x0e] == False:
- LocationsAvailable.append(0x0e)
- if HasJewelOfOpen and HasMist and (HasBat or HasPowerOfMist or (HasLeapStone and HasGravityBoots)) and (HasPowerOfMist or HasSonar):
- #Castle 2 - Flight, Mist, Jewel of Open, and sonar or power of mist
- if LocationsUsed[0x03] == False:
- LocationsAvailable.append(0x03)
- if LocationsUsed[0x09] == False:
- LocationsAvailable.append(0x09)
- if LocationsUsed[0x17] == False:
- LocationsAvailable.append(0x17)
- if LocationsUsed[0x18] == False:
- LocationsAvailable.append(0x18)
- if LocationsUsed[0x19] == False:
- LocationsAvailable.append(0x19)
- if LocationsUsed[0x1a] == False:
- LocationsAvailable.append(0x1a)
- if LocationsUsed[0x1b] == False:
- LocationsAvailable.append(0x1b)
- ThisRel = FindUnplacedRelic()
- if len(LocationsAvailable) == 1:
- #Only one location left?
- #Check to see if its the last item in the game
- #If not, give an item that will unlock more items
- #I need to actually think this through and place the correct items, but this will do for now
- if HasJewelOfOpen == False:
- ThisRel = 0x10
- elif HasLeapStone == False:
- ThisRel = 0x0D
- elif HasGravityBoots == False:
- ThisRel = 0x0C
- elif HasBat == False:
- ThisRel = 0x00
- elif HasMist == False:
- ThisRel = 0x07
- elif HasMermanStatue == False:
- ThisRel = 0x11
- else:
- ThisRel = FindUnplacedRelic()
- ThisLoc = FindUnplacedLocation(LocationsAvailable)
- #Items are never allowed in these locations
- if ThisRel == 0x02:
- if ThisLoc == 0x18 or ThisLoc == 0x19 or ThisLoc == 0x1a or ThisLoc == 0x1b or ThisLoc == 0x1c:
- return SoftUnlock()
- elif ThisRel == 0x07:
- if ThisLoc == 0x18 or ThisLoc == 0x19 or ThisLoc == 0x1a or ThisLoc == 0x1b or ThisLoc == 0x1c or ThisLoc == 0x00:
- return SoftUnlock()
- elif ThisRel == 0x10:
- if ThisLoc == 0x18 or ThisLoc == 0x19 or ThisLoc == 0x1a or ThisLoc == 0x1b or ThisLoc == 0x1c or ThisLoc == 0x0d or ThisLoc == 0x0e or ThisLoc == 0x11 or ThisLoc == 0x15:
- return SoftUnlock()
- elif ThisRel == 0x11:
- if ThisLoc == 0x0e:
- return SoftUnlock()
- retval = [ThisRel, ThisLoc]
- return retval
- def main():
- print("Sotn Relic Randomizer")
- print("Your file name should be \""+FileName+"\"")
- print("To show spoilers, edit the script and set ShowItemPlacements to True")
- print("If this is your first time running, you will need to download error_recalc.exe and put it in the same directory as this script. You can grab it here: https://www.romhacking.net/utilities/1264/")
- print("")
- random.seed(RandoSeed)
- print("Seed is \""+str(RandoSeed)+"\"")
- #Do some shuffling things, make sure things arent impossible to access
- #Make things always possible later
- print("Shuffling Relics..")
- #Place the rest of the items
- for i in range(0, len(RelicList)):
- PlsNoSoftlock = SoftUnlock()
- ThisRelic = PlsNoSoftlock[0]
- ThisLocation = PlsNoSoftlock[1]
- PlaceItem(ThisRelic, ThisLocation)
- print("Bytes Written, Fixing ECC..")
- #Windows
- #call(["error_recalc.exe", FileName, "1"])
- #Not Windows
- call(["wine", "error_recalc.exe", FileName, "1"])
- print("Done")
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement