Advertisement
Guest User

Aria of Sorrow - HP display

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