Advertisement
Guest User

Untitled

a guest
Oct 18th, 2017
406
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.89 KB | None | 0 0
  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()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement