daily pastebin goal
40%
SHARE
TWEET

Untitled

a guest Oct 18th, 2017 336 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #Lets Randomize Relics
  2. #Made by setz
  3.  
  4. #@splixel on twitter
  5. #twitch.tv/skiffain
  6.  
  7. #too lazy for licenses, pretend I attached WTFPL
  8. #(do what the fuck you want with this)
  9.  
  10. #error_recalc comes from https://www.romhacking.net/utilities/1264/
  11.  
  12. #Conditions for flight are one of the following
  13.     #Soul of Bat (ez mode)
  14.     #Gravity Boots + Leap Stone (chaining gravity jumps)
  15.     #Form of Mist + Power of Mist (fly as mist)
  16.  
  17. #Requirements for accessing castle 2 are
  18.     #Flight
  19.     #Jewel of Open
  20.     #Mist
  21.  
  22. import random
  23. from subprocess import call
  24. from binascii import hexlify
  25. from datetime import datetime
  26.  
  27.  
  28. FileName = "Castlevania - Symphony of the Night (USA) (Track 1).bin"
  29. #RandomizeBossRelics = False #Not Supported
  30.  
  31. ShowItemPlacements = True #Print out a log of when items are placed
  32.  
  33. #RandoSeed = 1234567890
  34. RandoSeed = datetime.time(datetime.now())
  35.  
  36.  
  37. #Ability Checks
  38. HasLeapStone = False
  39. HasGravityBoots = False
  40. HasJewelOfOpen = False
  41. HasMist = False
  42. HasPowerOfMist = False
  43. HasBat = False
  44. HasWolf = False
  45. HasSonar = False
  46. HasMermanStatue = False
  47.  
  48. #TODO
  49.     #accept args
  50.     #args: Player Seed input
  51.     #args for other options
  52.     #Options to not randomize boss relics  
  53.     #Copy files instead of just overwriting
  54.     #do a hash check to ensure its editing the right file
  55.     #possibly make a small frontend for it?
  56.  
  57. #Known Bugs
  58.     #need to trace relics from left/right as well as up/down because of how sotn loads entities
  59.         #List of known delinquents
  60.         #Cube Of Zoe
  61.         #Spirit Orb
  62.         #Farie Scroll
  63.         #Leap Stone
  64.    
  65. #Some relics have doubles, so..
  66.     #Relic ID/Name          #RelicLocation ID   #Cant Be Behind             RL ID No-Gos
  67.     #00 Soul of Bat         00                 
  68.     #01 Fire of Bat         01                 
  69.     #02 Echo of Bat         02                  Castle 2                    18 19 1a 1b 1c
  70.     #03 Force of Echo       03                 
  71.     #04 Soul of Wolf        04                 
  72.     #05 Power of Wolf       05                 
  73.     #05 Power of Wolf       05                 
  74.     #06 Skill of Wolf       06                 
  75.     #07 Form of Mist        07                  Castle 2, Mist Gates        18 19 1a 1b 1c 00
  76.     #08 Power of Mist       08                 
  77.     #09 Gas Cloud           09                 
  78.     #0A Cube of Zoe         0a                 
  79.     #0A Cube of Zoe         0a                 
  80.     #0B Spirit Orb          0b                 
  81.     #0C Gravity Boots       0c                 
  82.     #0D Leap Stone          0d                 
  83.     #0E Holy Symbol         0e                 
  84.     #0F Faerie Scroll       0f                 
  85.     #10 Jewel of Open       10                  Castle 2, Jewel Doors       18 19 1a 1b 1c 0d 0e 11 15
  86.     #11 Merman Statue       11                  Holy Snorkel Location       0e
  87.     #12 Bat Card            12                 
  88.     #13 Ghost Card          13                 
  89.     #14 Faerie Card         14                 
  90.     #15 Demon Card          15                 
  91.     #16 Sword Card          16                 
  92.     #17 Sprite Card         --                 
  93.     #18 Nosedevil Card      --                 
  94.     #19 Heart of Vlad       17                 
  95.     #19 Heart of Vlad       17                 
  96.     #1A Tooth of Vlad       18                 
  97.     #1A Tooth of Vlad       18                 
  98.     #1B Rib of Vlad         19                 
  99.     #1B Rib of Vlad         19                 
  100.     #1C Ring of Vlad        1a                 
  101.     #1C Ring of Vlad        1a                 
  102.     #1D Eye of Vlad         1b                 
  103.     #1D Eye of Vlad         1b                 
  104. RelicLocation = [0x047a5b66, 0x0557535e, 0x04aa4156, 0x0526e6a8, 0x049d6596, 0x04b6b9b4, 0x054b1d5a, 0x043c578a,
  105.     0x05610db8, 0x04cfcb16, 0x04b6b946, 0x048fd1fe, 0x048fc9ba, 0x05610dc2, 0x04c34ee6, 0x047a5720,
  106.     0x047a321c, 0x04c35174, 0x054b1d58, 0x05611958, 0x047a5784, 0x045ea95e, 0x04aa3f76, 0x06306ab2,
  107.     0x05051d52, 0x069d2b1e, 0x059bdb30, 0x04da65f2]
  108. DoubleLocation = [0, 0, 0, 0, 0, 0x053f971c, 0, 0,
  109.     0x0561142C, 0, 0x053F969A, 0, 0, 0, 0, 0,
  110.     0, 0, 0, 0x0561127c, 0, 0, 0, 0x04e335b4,
  111.     0x067d1630, 0x050fa914, 0x059ee2e4, 0x0662263a]
  112. TripleLocation = [0, 0, 0, 0, 0, 0, 0, 0,
  113.     0, 0, 0x04b6b08a, 0x048fe280, 0, 0, 0, 0,
  114.     0, 0, 0, 0, 0, 0, 0, 0,
  115.     0, 0, 0, 0]
  116. QuadrupleLocation = [0, 0, 0, 0, 0, 0, 0, 0,
  117.     0, 0, 0x053f8e2e, 0, 0, 0, 0, 0,
  118.     0, 0, 0, 0, 0, 0, 0, 0,
  119.     0, 0, 0, 0]
  120. RelicList = []
  121. RelicList = [bytearray([0x00]),
  122.     bytearray([0x01]),
  123.     bytearray([0x02]),
  124.     bytearray([0x03]),
  125.     bytearray([0x04]),
  126.     bytearray([0x05]),
  127.     bytearray([0x06]),
  128.     bytearray([0x07]),
  129.     bytearray([0x08]),
  130.     bytearray([0x09]),
  131.     bytearray([0x0a]),
  132.     bytearray([0x0b]),
  133.     bytearray([0x0c]),
  134.     bytearray([0x0d]),
  135.     bytearray([0x0e]),
  136.     bytearray([0x0f]),
  137.     bytearray([0x10]),
  138.     bytearray([0x11]),
  139.     bytearray([0x12]),
  140.     bytearray([0x13]),
  141.     bytearray([0x14]),
  142.     bytearray([0x15]),
  143.     bytearray([0x16]),
  144.     bytearray([0x19]),
  145.     bytearray([0x1a]),
  146.     bytearray([0x1b]),
  147.     bytearray([0x1c]),
  148.     bytearray([0x1d])]
  149.  
  150. RelicsUsed = []
  151. LocationsUsed = []
  152. for i in range(0, len(RelicList)):
  153.     RelicsUsed.append(False)
  154.     LocationsUsed.append(False)
  155.  
  156. def ReplaceByte(ByteLocation, NewByte):
  157.     with file(FileName, "r+b") as HackThisRom:
  158.         HackThisRom.seek(ByteLocation)
  159.         HackThisRom.write(NewByte)
  160.     return True
  161.  
  162. def PlaceItem(Item, Location):
  163.     if ShowItemPlacements:
  164.         print("Placing Item: "+hexlify(RelicList[Item]))
  165.         #print("Location a: "+str(hex(RelicLocation[Location])))
  166.     ReplaceByte(RelicLocation[Location], RelicList[Item])
  167.  
  168.     if (DoubleLocation[Location] != 0):
  169.         #if ShowItemPlacements:
  170.         #   print("Location b: "+str(hex(DoubleLocation[Location])))
  171.         ReplaceByte(DoubleLocation[Location], RelicList[Item])     
  172.  
  173.     if (TripleLocation[Location] != 0):
  174.         #if ShowItemPlacements:
  175.         #   print("Location b: "+str(hex(DoubleLocation[Location])))
  176.         ReplaceByte(TripleLocation[Location], RelicList[Item])     
  177.  
  178.     if (QuadrupleLocation[Location] != 0):
  179.         #if ShowItemPlacements:
  180.         #   print("Location b: "+str(hex(DoubleLocation[Location])))
  181.         yReplaceByte(QuadrupleLocation[Location], RelicList[Item])     
  182.    
  183.     #Check abilities if possible
  184.     global HasJewelOfOpen
  185.     global HasLeapStone
  186.     global HasMist
  187.     global HasPowerOfMist
  188.     global HasGravityBoots
  189.     global HasBat
  190.     global HasWolf
  191.     global HasSonar
  192.     global HasMermanStatue
  193.  
  194.     if Item == 0x10:
  195.         HasJewelOfOpen = True
  196.     elif Item == 0xd:
  197.         HasLeapStone = True
  198.     elif Item == 0x7:
  199.         HasMist = True
  200.     elif Item == 0x8:
  201.         HasPowerOfMist = True
  202.     elif Item == 0xc:
  203.         HasGravityBoots = True
  204.     elif Item == 0x4:
  205.         HasWolf = True
  206.     elif Item == 0x0:
  207.         HasBat = True
  208.     elif Item == 0x2:
  209.         HasSonar = True
  210.     elif Item == 0x11:
  211.         HasMermanStatue = True
  212.  
  213.     #Mark as used
  214.     RelicsUsed[Item] = True
  215.     LocationsUsed[Location] = True
  216.     return True
  217.  
  218. def FindUnplacedRelic():
  219.     #print(len(RelicList))
  220.     RandIndex = random.randint(0, len(RelicList)-1)
  221.     if RelicsUsed[RandIndex]:
  222.         return FindUnplacedRelic()
  223.     else:
  224.         return RandIndex
  225.  
  226. def FindUnplacedLocation(InputArray):
  227.     RandIndex = InputArray[random.randint(0, len(InputArray)-1)]
  228.     if LocationsUsed[RandIndex]:
  229.         return FindUnplacedLocation(InputArray)
  230.     else:
  231.         return RandIndex
  232.  
  233. def SoftUnlock():
  234.     #print(str(HasJewelOfOpen)+" | "+str(HasLeapStone)+" | "+str(HasMist)+" | "+str(HasPowerOfMist)+" | "+str(HasGravityBoots)+" | "+str(HasBat)+" | "+str(HasSonar)+" | "+str(HasMermanStatue))
  235.     #List of available locations
  236.     LocationsAvailable = []
  237.     #Starting Areas
  238.     if LocationsUsed[0x04] == False:
  239.         LocationsAvailable.append(0x04)
  240.     if LocationsUsed[0x0a] == False:
  241.         LocationsAvailable.append(0x0a)
  242.     if LocationsUsed[0x0b] == False:
  243.         LocationsAvailable.append(0x0b)
  244.     if LocationsUsed[0x0f] == False:
  245.         LocationsAvailable.append(0x0f)
  246.     if LocationsUsed[0x10] == False:
  247.         LocationsAvailable.append(0x10)
  248.    
  249.     #Restricted Areas
  250.     if HasMist and (HasLeapStone or HasGravityBoots or HasBat):
  251.         #Soul of Bat Vanilla
  252.         if LocationsUsed[0x00] == False:
  253.             LocationsAvailable.append(0x00)
  254.     if HasBat or (HasGravityBoots and HasLeapStone) or (HasMist and HasPowerOfMist):
  255.         #Flight only
  256.         if LocationsUsed[0x01] == False:
  257.             LocationsAvailable.append(0x01)
  258.         if LocationsUsed[0x05] == False:
  259.             LocationsAvailable.append(0x05)
  260.         if LocationsUsed[0x08] == False:
  261.             LocationsAvailable.append(0x08)
  262.         if LocationsUsed[0x0c] == False:
  263.             LocationsAvailable.append(0x0c)
  264.         if LocationsUsed[0x13] == False:
  265.             LocationsAvailable.append(0x13)
  266.         if LocationsUsed[0x16] == False:
  267.             LocationsAvailable.append(0x16)
  268.     if (HasBat or (HasMist and HasPowerOfMist) or (HasGravityBoots and HasLeapStone)) and (HasMist or HasWolf or HasBat):
  269.         if LocationsUsed[0x02] == False: #Olrox's Prize
  270.             LocationsAvailable.append(0x02)
  271.     if HasGravityBoots or HasBat or (HasMist and HasPowerOfMist):
  272.         #Gravity Boots or better
  273.         if LocationsUsed[0x06] == False:
  274.             LocationsAvailable.append(0x06)
  275.         if LocationsUsed[0x12] == False:
  276.             LocationsAvailable.append(0x12)
  277.         if LocationsUsed[0x14] == False:
  278.             LocationsAvailable.append(0x14)
  279.     if HasLeapStone or HasGravityBoots or HasBat or (HasMist and HasPowerOfMist):
  280.         #Leapstone or better
  281.         if LocationsUsed[0x07] == False:
  282.             LocationsAvailable.append(0x07)
  283.         if LocationsUsed[0x0d] == False:
  284.             LocationsAvailable.append(0x0d) #Colosseum - only required if leap stone?
  285.     if HasJewelOfOpen:
  286.         #Bottom of the castle
  287.         if LocationsUsed[0x11] == False:
  288.             LocationsAvailable.append(0x11)
  289.     if HasJewelOfOpen and HasLeapStone or HasBat or (HasMist and HasPowerOfMist):
  290.         if LocationsUsed[0x15] == False: #Demon card a bitch
  291.             LocationsAvailable.append(0x15)
  292.     if HasMermanStatue and HasJewelOfOpen:
  293.         #holy snorkel vanilla
  294.         if LocationsUsed[0x0e] == False:
  295.             LocationsAvailable.append(0x0e)
  296.     if HasJewelOfOpen and HasMist and (HasBat or HasPowerOfMist or (HasLeapStone and HasGravityBoots)) and (HasPowerOfMist or HasSonar):
  297.         #Castle 2 - Flight, Mist, Jewel of Open, and sonar or power of mist
  298.         if LocationsUsed[0x03] == False:
  299.             LocationsAvailable.append(0x03)
  300.         if LocationsUsed[0x09] == False:
  301.             LocationsAvailable.append(0x09)
  302.         if LocationsUsed[0x17] == False:
  303.             LocationsAvailable.append(0x17)
  304.         if LocationsUsed[0x18] == False:
  305.             LocationsAvailable.append(0x18)
  306.         if LocationsUsed[0x19] == False:
  307.             LocationsAvailable.append(0x19)
  308.         if LocationsUsed[0x1a] == False:
  309.             LocationsAvailable.append(0x1a)
  310.         if LocationsUsed[0x1b] == False:
  311.             LocationsAvailable.append(0x1b)
  312.  
  313.     ThisRel = FindUnplacedRelic()
  314.     if len(LocationsAvailable) == 1:
  315.         #Only one location left?
  316.         #Check to see if its the last item in the game
  317.         #If not, give an item that will unlock more items
  318.         #I need to actually think this through and place the correct items, but this will do for now
  319.         if HasJewelOfOpen == False:
  320.             ThisRel = 0x10
  321.         elif HasLeapStone == False:
  322.             ThisRel = 0x0D
  323.         elif HasGravityBoots == False:
  324.             ThisRel = 0x0C
  325.         elif HasBat == False:
  326.             ThisRel = 0x00
  327.         elif HasMist == False:
  328.             ThisRel = 0x07
  329.         elif HasMermanStatue == False:
  330.             ThisRel = 0x11
  331.     else:
  332.         ThisRel = FindUnplacedRelic()
  333.  
  334.     ThisLoc = FindUnplacedLocation(LocationsAvailable)
  335.  
  336.     #Items are never allowed in these locations
  337.     if ThisRel == 0x02:
  338.         if ThisLoc == 0x18 or ThisLoc == 0x19 or ThisLoc == 0x1a or ThisLoc == 0x1b or ThisLoc == 0x1c:
  339.             return SoftUnlock()
  340.     elif ThisRel == 0x07:
  341.         if ThisLoc == 0x18 or ThisLoc == 0x19 or ThisLoc == 0x1a or ThisLoc == 0x1b or ThisLoc == 0x1c or ThisLoc == 0x00:
  342.             return SoftUnlock()
  343.     elif ThisRel == 0x10:
  344.         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:
  345.             return SoftUnlock()
  346.     elif ThisRel == 0x11:
  347.         if ThisLoc == 0x0e:
  348.             return SoftUnlock()
  349.  
  350.     retval = [ThisRel, ThisLoc]
  351.     return retval
  352.  
  353. def main():
  354.     print("Sotn Relic Randomizer")
  355.     print("Your file name should be \""+FileName+"\"")
  356.     print("To show spoilers, edit the script and set ShowItemPlacements to True")
  357.     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/")
  358.     print("")
  359.    
  360.     random.seed(RandoSeed)
  361.     print("Seed is \""+str(RandoSeed)+"\"")
  362.  
  363.     #Do some shuffling things, make sure things arent impossible to access
  364.     #Make things always possible later
  365.     print("Shuffling Relics..")
  366.  
  367.     #Place the rest of the items
  368.     for i in range(0, len(RelicList)):
  369.         PlsNoSoftlock = SoftUnlock()
  370.         ThisRelic = PlsNoSoftlock[0]
  371.         ThisLocation = PlsNoSoftlock[1]
  372.         PlaceItem(ThisRelic, ThisLocation)
  373.  
  374.     print("Bytes Written, Fixing ECC..")
  375.  
  376.     #Windows
  377.     #call(["error_recalc.exe", FileName, "1"])
  378.  
  379.     #Not Windows
  380.     call(["wine", "error_recalc.exe", FileName, "1"])
  381.  
  382.     print("Done")
  383.  
  384. if __name__ == '__main__':
  385.     main()
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top