Advertisement
Anty-Lemon

CV:AoS Drop Script

Nov 19th, 2015
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.35 KB | None | 0 0
  1. -- Castlevania: Aria of Sorrow - Drop Script by rhebus
  2. -- Adapted for LuaHawk
  3.  
  4. local RAM = {
  5.             -- EWRAM
  6.             RNG        = 0x000008,
  7.             DIFFICULTY = 0x0000a1,
  8.             ENEMY      = 0x0012d0,
  9.             PLAYER     = 0x013110,
  10.             BULLETS    = 0x01331c,
  11.             GUARDIANS  = 0x013354,
  12.             ENCHANTS   = 0x01336e,
  13.             ABILITIES  = 0x013392,
  14.             -- ROM
  15.             ENEMY_LOOT_BASE = 0x0e9644,
  16. }
  17. local MAX_ENEMY = 63
  18. local ENEMY_SIZE = 0x84
  19.  
  20. local Enemy_offset = {
  21.     Bits = { offset = 0x3e, size = 1 },
  22.     Type = { offset = 0x36, size = 1 },
  23.     HP   = { offset = 0x34, size = 2, signed = true },
  24.     MP   = { offset = 0x2e, size = 2 },
  25.     xpos = { offset = 0x40, size = 4, signed = true },
  26.     ypos = { offset = 0x44, size = 4, signed = true },
  27.     valid= { offset = 0x64, size = 1 },
  28.     cooldown1      = { offset = 0x78, size = 1 },
  29.     cooldown2      = { offset = 0x79, size = 1 },
  30.     cooldown3      = { offset = 0x7a, size = 1 },
  31.     cooldown4      = { offset = 0x7b, size = 1 },
  32.     cooldown_type1 = { offset = 0x74, size = 1 },
  33.     cooldown_type2 = { offset = 0x75, size = 1 },
  34.     cooldown_type3 = { offset = 0x76, size = 1 },
  35.     cooldown_type4 = { offset = 0x77, size = 1 },
  36. }
  37.  
  38. local Enemy_loot_offset = {
  39.     Common_type   = { offset = 0x08, size = 2 },
  40.     Common_rarity = { offset = 0x15, size = 1 },
  41.     Rare_type     = { offset = 0x0a, size = 2 },
  42.     Rare_rarity   = { offset = 0x16, size = 1 },
  43.     Soul_rarity   = { offset = 0x12, size = 1 },
  44.     Soul_type     = { offset = 0x17, size = 1 }, -- bullet, guardian etc
  45.     Soul_index    = { offset = 0x18, size = 1 },
  46. }
  47.  
  48. local Player_offset = {
  49.     LCK            = { offset = 0xe8,  size = 2 },
  50.     Accessory_type = { offset = 0x15d, size = 1 },
  51. }
  52.  
  53. local Cooldown_types = {
  54.     '?',
  55.     'K',
  56.     'W',
  57.     'S',
  58.     'B',
  59.     'G',
  60. }
  61.  
  62. local Enemy_names = {
  63. "Bat", "Zombie", "Skeleton", "Merman", "Axe Armor",
  64. "Skull Archer", "Peeping Eye", "Killer Fish", "Bone Pillar", "Blue Crow",
  65. "Buer", "White Dragon", "Zombie Soldier", "Skeleton Knight", "Ghost",
  66. "Siren", "Tiny Devil", "Durga", "Rock Armor", "Giant Ghost",
  67. "Winged Skeleton", "Minotaur", "Student Witch", "Arachne", "Fleaman",
  68. "Evil Butcher", "Quezlcoatl", "Ectoplasm", "Catoblepas", "Ghost Dancer",
  69. "Waiter Skeleton", "Killer Doll", "Zombie Officer", "Creaking Skull", "Wooden Golem",
  70. "Tsuchinoko", "Persephone", "Lilith", "Nemesis", "Kyoma Demon",
  71. "Chronomage", "Valkyrie", "Witch", "Curly", "Altair",
  72. "Red Crow", "Cockatrice", "Dead Warrior", "Devil", "Imp",
  73. "Werewolf", "Gorgon", "Disc Armor", "Golem", "Manticore",
  74. "Gremlin", "Harpy", "Medusa Head", "Bomber Armor", "Lightning Doll",
  75. "Great Armor", "Une", "Giant Worm", "Needles", "Man-Eater",
  76. "Fish Head", "Nightmare", "Triton", "Slime", "Big Golem",
  77. "Dryad", "Poison Worm", "Arc Demon", "Cagnazzo", "Ripper",
  78. "Werejaguar", "Ukoback", "Alura Une", "Biphron", "Mandragora",
  79. "Flesh Golem", "Sky Fish", "Dead Crusader", "Kicker Skeleton", "Weretiger",
  80. "Killer Mantle", "Mudman", "Gargoyle", "Red Minotaur", "Beam Skeleton",
  81. "Alastor", "Skull Millione", "Giant Skeleton", "Gladiator", "Bael",
  82. "Succubus", "Mimic", "Stolas", "Erinys", "Lubicant",
  83. "Basilisk", "Iron Golem", "Demon Lord", "Final Guard", "Flame Demon",
  84. "Shadow Knight", "Headhunter", "Death", "Legion", "Balore",
  85. "Belmont", "Graham", "Chaos",
  86. }
  87.  
  88. --[ AoS RNG simulator ] --------------------------------------------------------
  89. -- Original code by gocha
  90.  
  91. local function AoS_RNG_func(seed)
  92.     seed = bit.rshift(seed, 8)
  93.     return bit.band(bit.lshift(0x3243 * seed, 16) + 0xf6ad * seed + 0x1b0cb175, 0xffffffff)
  94. end
  95.  
  96. --------------------------------------------------------------------------------
  97.  
  98. local function mem_read(addr,size,signed)
  99.     if size == 1 then
  100.         if signed then
  101.             return memory.read_s8(addr)
  102.         else
  103.             return memory.read_u8(addr)
  104.         end
  105.     elseif size == 2 then
  106.         if signed then
  107.             return memory.read_s16_le(addr)
  108.         else
  109.             return memory.read_u16_le(addr)
  110.         end
  111.     elseif size == 4 then
  112.         if signed then
  113.             return memory.read_s32_le(addr)
  114.         else
  115.             return memory.read_u32_le(addr)
  116.         end
  117.     else
  118.         error("invalid size")
  119.     end
  120. end
  121.  
  122. local function soul_table_read(tab,index)
  123.     local entry  = bit.rshift(index,1)
  124.     local nibble = index % 2
  125.     local byte   = mem_read(tab+entry,1,0)
  126.     if (nibble == 1) then
  127.         return bit.rshift(byte,4)
  128.     else
  129.         return byte % 0x10
  130.     end
  131. end
  132.  
  133. local function enemy_read(base,attr)
  134.     local loc = Enemy_offset[attr]
  135.     return mem_read(base + loc.offset,loc.size,loc.signed)
  136. end
  137.  
  138. -- precache all monster loot tables, since they're stored in ROM
  139. local Monster_loot = {}
  140. memory.usememorydomain("ROM")
  141.  
  142. for mon_id = 0,112 do
  143.     local loottable = {}
  144.     for attr, details in pairs(Enemy_loot_offset) do
  145.         local mon_base = RAM.ENEMY_LOOT_BASE + 0x24 * mon_id
  146.         local loc = Enemy_loot_offset[attr]
  147.         loottable[attr] = mem_read(
  148.             mon_base + loc.offset, loc.size, loc.signed
  149.             )
  150.     end
  151.     table.insert(Monster_loot, loottable)
  152. end
  153.  
  154. memory.usememorydomain("EWRAM")
  155.  
  156. local function monster_loot(mon_id, attr)
  157.     return Monster_loot[mon_id+1][attr]
  158. end
  159.  
  160.  
  161. local function player_read(attr)
  162.     local loc = Player_offset[attr]
  163.     return mem_read(RAM.PLAYER + loc.offset,loc.size,loc.signed)
  164. end
  165.  
  166. local function enemy_type_as_string(mon_id)
  167.     return Enemy_names[mon_id+1] or 'N/A'
  168. end
  169.  
  170. local function enemy_cooldownstring(base)
  171.     local cooldowns = {
  172.         {enemy_read(base,"cooldown1"),enemy_read(base,"cooldown_type1")},
  173.         {enemy_read(base,"cooldown2"),enemy_read(base,"cooldown_type2")},
  174.         {enemy_read(base,"cooldown3"),enemy_read(base,"cooldown_type3")},
  175.         {enemy_read(base,"cooldown4"),enemy_read(base,"cooldown_type4")},
  176.     }
  177.     table.sort(cooldowns,function(a,b) return a[2] < b[2] end)
  178.     local str = ""
  179.     for i,v in ipairs(cooldowns) do
  180.         if v[2] ~= 0 then
  181.             str = str .. string.format("%d%s ", v[1], Cooldown_types[v[2]])
  182.         end
  183.     end
  184.     return str
  185. end
  186.  
  187. local function enemy_string(base)
  188.     local mon_id = enemy_read(base,"Type")
  189.     return string.format("%dH %s%s",
  190.         enemy_read(base,"HP"),
  191.         enemy_cooldownstring(base),
  192.         enemy_type_as_string(mon_id))
  193. end
  194.  
  195. -- list all outcomes of upcoming random numbers on function f
  196. local function list_rng(f)
  197.     local searchMax = 40
  198.     local rng_seed = memory.read_u32_le(RAM.RNG)
  199.     local retstr = ""
  200.     for i = 0, searchMax do
  201.         local result = f(rng_seed)
  202.         if result then
  203.             retstr = retstr .. i .. result .. " "
  204.         end
  205.         rng_seed = AoS_RNG_func(rng_seed)
  206.     end
  207.     return retstr
  208. end
  209.  
  210. local function souls_collected(mon_id)
  211.     local soul_type = monster_loot(mon_id,"Soul_type")
  212.     local soul_index= monster_loot(mon_id,"Soul_index")
  213.     if soul_index == 0 then return nil end
  214.     local tab
  215.     if soul_type == 0 then
  216.         tab = RAM.BULLETS
  217.     elseif soul_type == 1 then
  218.         tab = RAM.GUARDIANS
  219.     elseif soul_type == 2 then
  220.         tab = RAM.ENCHANTS
  221.     elseif soul_type == 3 then
  222.         tab = RAM.ABILITIES
  223.     else
  224.         return nil
  225.     end
  226.     return soul_table_read(tab,soul_index-1)
  227. end
  228.  
  229. local function wearing_rare_ring()
  230.     return player_read("Accessory_type") == 0x27
  231. end
  232.  
  233. local function wearing_soul_ring()
  234.     return player_read("Accessory_type") == 0x28
  235. end
  236.  
  237. local function wearing_gold_ring()
  238.     return player_read("Accessory_type") == 0x2b
  239. end
  240.  
  241. local function wearing_heart_pendant()
  242.     return player_read("Accessory_type") == 0x1d
  243. end
  244.  
  245. local function hard_difficulty()
  246.     return mem_read(RAM.DIFFICULTY,1,0) >= 0x10
  247. end
  248.  
  249.  
  250. -- create a loot function to evaluate rng values for a given enemy
  251. local function loot_func(mon_id, luck)
  252.     -- cache useful values
  253.     local soul_rarity   = monster_loot(mon_id,"Soul_rarity")
  254.     local soul_index    = monster_loot(mon_id,"Soul_index")
  255.     local rare_rarity   = monster_loot(mon_id,"Rare_rarity")
  256.     local rare_type     = monster_loot(mon_id,"Rare_type"  )
  257.     local common_rarity = monster_loot(mon_id,"Common_rarity")
  258.     local common_type   = monster_loot(mon_id,"Common_type"  )
  259.     local rare_ring     = wearing_rare_ring()
  260.     local soul_ring     = wearing_soul_ring()
  261.     local heart_pendant = wearing_heart_pendant()
  262.     local gold_ring     = wearing_gold_ring()
  263.     local drops         = common_type ~= 0 or rare_type ~= 0
  264.     return function(initial_rng)
  265.         local rng = initial_rng
  266.         --FIXME
  267.         if (soul_rarity ~= 0 and soul_index ~= 0) then
  268.             local target = 3
  269.             if souls_collected(mon_id) == 0 then
  270.                 if hard_difficulty() then
  271.                     target = 7
  272.                 else
  273.                     target = 6
  274.                 end
  275.             end
  276.             if soul_ring then target = target + 8 end
  277.             local range = 32 + soul_rarity * 8 - bit.rshift(luck,4)
  278.             if range < 16 then range = 16 end
  279.  
  280.             if ((bit.rshift(rng,2) % range) < target) then
  281.                 return 's' -- win!
  282.             end
  283.             rng = AoS_RNG_func(rng)
  284.  
  285.         end
  286.         -- can't drop anything else unless monster has either a common or a
  287.         -- rare drop
  288.         if drops then
  289.             -- choose common or rare
  290.             local target = 0x40
  291.             local item_rarity = common_rarity
  292.             local item_type   = common_type
  293.             local item_code   = 'c'
  294.             if rare_ring then target = 0x60 end
  295.             if bit.rshift(rng,1) % 256 < target then
  296.                 item_rarity = rare_rarity
  297.                 item_type   = rare_type
  298.                 item_code   = 'r'
  299.             end
  300.             rng = AoS_RNG_func(rng)
  301.  
  302.             if item_type ~= 0 then
  303.                 local drop_target = 4
  304.                 if rare_ring then drop_target = 8 end
  305.                 local range = 16 + item_rarity * 4 - bit.rshift(luck,4)
  306.                 if range < 16 then range = 16 end
  307.                 if ((bit.rshift(rng,3) % range) < drop_target) then
  308.                     return item_code -- win!
  309.                 end
  310.                 rng = AoS_RNG_func(rng)
  311.             end
  312.             -- can't drop gold or hearts unless you can also drop items
  313.             local gold_target = 7
  314.             if gold_ring then gold_target = 0x3f end
  315.             if bit.rshift(rng,3) % 0x400 <= target then
  316.                 -- to report gold drops, change this line to return 'g'
  317.                 return nil
  318.             end
  319.             rng = AoS_RNG_func(rng)
  320.  
  321.             local heart_target = 7
  322.             if heart_pendant then heart_target = 0x3f end
  323.             if bit.rshift(rng,4) % 0x400 <= target then
  324.                 -- large or small?
  325.                 rng = AoS_RNG_func(rng)
  326.                 if bit.band(rng,0xc0) == 0 then
  327.                     return 'H'
  328.                 else
  329.                     return 'h'
  330.                 end
  331.             end
  332.         end
  333.         return nil
  334.     end
  335. end
  336.  
  337. local function loot_string(mon_id)
  338.     local monster_type = mon_id
  339.     return string.format("%s%s",list_rng(
  340.                                     loot_func(monster_type, player_read("LCK"))
  341.                                 ),
  342.                                 enemy_type_as_string(mon_id))
  343. end
  344.  
  345.  
  346. local enemy_table = {}
  347. local screen = {}
  348.  
  349. event.onframeend(function()
  350.     screen.x_add = client.borderwidth()
  351.     screen.y_add = client.borderheight()
  352.     screen.x_mul = (client.screenwidth() - screen.x_add * 2) / 240
  353.     screen.y_mul = (client.screenheight() - screen.y_add * 2) / 160
  354.  
  355.     -- calculate monster table
  356.     enemy_table = {}
  357.     local num_monsters = 0
  358.     for i=0,MAX_ENEMY do
  359.         local enemy = RAM.ENEMY + i*ENEMY_SIZE
  360.         local mon_id = enemy_read(enemy,"Type")
  361.         if enemy_read(enemy,"HP") > 0 then
  362.             table.insert(enemy_table, enemy)
  363.             gui.text(0, 19 * screen.y_mul + 12 * num_monsters + screen.y_add, loot_string(mon_id))
  364.             num_monsters = num_monsters + 1
  365.         end
  366.     end
  367.    
  368.     local i = 1
  369.     for num,enemy in pairs(enemy_table) do
  370.         -- print enemy info at the enemy's feet
  371.         gui.text(enemy_read(enemy, "xpos") / 0x10000 * screen.x_mul + screen.x_add, enemy_read(enemy, "ypos") / 0x10000 * screen.y_mul + screen.y_add,
  372.             string.format("%d/%d", enemy_read(enemy, "HP"), enemy_read(enemy, "MP")))
  373.         i = i+1
  374.     end
  375. end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement