<?xml version='1.0' encoding='utf-8'?>
<CheatTable CheatEngineTableVersion="45">
  <CheatEntries>
    <CheatEntry>
      <ID>1</ID>
      <Description>"Auto-Attach and Version Check"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>4</ID>
      <Description>"☕ Fuel the Cheats — Tip Katzy (Toggle to open my Ko-fi page)"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>2</ID>
      <Description>"How to use - Katzy"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>3</ID>
      <Description>"Clear All"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>10</ID>
      <Description>"[MAIN] Bloodstained: Curse of the Moon"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
      <Options moHideChildren="1" />
      <CheatEntries>
        <CheatEntry>
          <ID>14</ID>
          <Description>"Infinite Lives"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>16</ID>
          <Description>"Invincibility"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>11</ID>
          <Description>"Unlimited Health"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>13</ID>
          <Description>"Infinite Jump"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>15</ID>
          <Description>"Always Charged Swings - Zangetsu"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>12</ID>
          <Description>"Unlimited Weapon Points"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>20</ID>
          <Description>"Subweapon Picker"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if not KatzyCOTM then error("Allow the table Lua script, then reopen this table.") end
{$asm}
[DISABLE]
</AssemblerScript>
          <Options moHideChildren="1" />
          <CheatEntries>
            <CheatEntry>
              <ID>19</ID>
              <Description>"Alfred"</Description>
              <DropDownList ReadOnly="1" DescriptionOnly="1" DisplayValueAsItem="1">1:Burning Sphere
2:Frostcalibur
3:Soul Vision
4:Voltaic Ray</DropDownList>
              <VariableType>Byte</VariableType>
              <Address>0</Address>
            </CheatEntry>
            <CheatEntry>
              <ID>18</ID>
              <Description>"Miriam"</Description>
              <DropDownList ReadOnly="1" DescriptionOnly="1" DisplayValueAsItem="1">1:Dagger
2:Rapier
3:Axe
4:Scythe</DropDownList>
              <VariableType>Byte</VariableType>
              <Address>0</Address>
            </CheatEntry>
            <CheatEntry>
              <ID>17</ID>
              <Description>"Zangetsu"</Description>
              <DropDownList ReadOnly="1" DescriptionOnly="1" DisplayValueAsItem="1">1:Chain
2:Talisman
3:Demon Essence</DropDownList>
              <VariableType>Byte</VariableType>
              <Address>0</Address>
            </CheatEntry>
          </CheatEntries>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
  </CheatEntries>
  <UserdefinedSymbols />
  <LuaScript>--[[
Table identification: Katzy
Bloodstained: Curse of the Moon / Steam build 2824730 / Table v1.15
Original patches derived from the installed game's running code.
]]
if KatzyCOTM and not KatzyCOTM.shutdown() then
  showMessage('The previous table could not restore the game. Reattach to the original game and use Clear All before loading this table.')
  return
end
local T={locks={},ledger={}, resolved={}, busy=false, lastError=nil}
KatzyCOTM=T
local patches={
{group='health', pattern='55 8B EC 8B 45 08 85 C0 74 59 8B 15 ?? ?? ?? ??', offset=8, old={116}, new={235}},
{group='points', pattern='A1 ?? ?? ?? ?? 8B 40 08 8A 40 1E C3', offset=10, old={30}, new={29}},
{group='points', pattern='2A 45 08 B9 00 00 00 00 0F B6 C0 0F 49 C8 A1 ?? ?? ?? ?? 8B 40 08 88 48 1E 5D C2 04 00', offset=22, old={136}, new={138}},
{group='points', pattern='2C 02 B9 00 00 00 00 0F B6 C0 0F 49 C8 A1 ?? ?? ?? ?? 68 80 00 00 00 51 C7 04 24 00 00 00 00 8B 40 08 68 03 80 00 00 68 71 2A 72 3D 88 48 1E', offset=44, old={136}, new={138}},
{group='points', pattern='2C 02 B9 00 00 00 00 0F B6 C0 0F 49 C8 A1 ?? ?? ?? ?? 8B 40 08 88 48 1E FE 46 18', offset=21, old={136}, new={138}},
{group='jump', pattern='56 8B F1 80 BE AC 03 00 00 00 75 19 F3 0F 10 86', offset=10, old={117}, new={235}},
{group='lives', pattern='8B 46 08 83 78 0C 01 74 0F 6A FF 8B CE E8 E9 8C 0C 00 8B 35 ?? ?? ??', offset=10, old={255}, new={0}},
{group='charge', pattern='A1 ?? ?? ?? ?? 8B 40 08 F7 40 2C 00 02 10 00 74 73', offset=8, old={247,64,44,0,2,16,0}, new={233,181,253,255,255,144,144}},
{group='invincible', pattern='F3 0F 10 81 3C 05 00 00 0F 2F 05 ?? ?? ?? ?? 76 15 B0 01 8B', offset=16, old={21}, new={0}},
{group='health', pattern='55 8B EC A1 ?? ?? ?? ?? 8B 4D 08 8B 50 08 8B 42 2C C1 E8 12', offset=8, old={139,77,8}, new={49,201,144}}
}
local records=getAddressList()
local function rec(id) return records.getMemoryRecordByID(id) end
local function notify(message)
  if T.lastError~=message then T.lastError=message; showMessage(message) end
end
local function module(pid)
  local ok,mods=pcall(enumModules,pid)
  if ok and mods then
    for _,m in ipairs(mods) do
      if m.Name:lower()=='cotm.exe' then return m end
    end
  end
end
local function alive(pid)
  local ok,list=pcall(getProcesslist)
  if not ok or not list then return nil end
  return list[pid]~=nil
end
local function byte(address)
  local ok,v=pcall(readBytes,address,1,false)
  if ok then return v end
end
local function equal(address,values)
  for i,v in ipairs(values) do if byte(address+i-1)~=v then return false end end
  return true
end
local function ownedBytes(address,p)
  for i,v in ipairs(p.old) do
    local current=byte(address+i-1)
    if current~=v and current~=p.new[i] then return false end
  end
  return true
end
local function write(address,value)
  local ok=pcall(writeBytes,address,value)
  return ok and equal(address,value)
end
local sameProcess
local function validate()
  if not AOBScanModuleUnique then return false,'Use Cheat Engine 7.6 or newer with this table.' end
  local pid=getProcessIDFromProcessName('COTM.exe')
  if not pid or pid==0 then return false,'Bloodstained: Curse of the Moon is not running. Start the game, then enable MAIN.' end
  if next(T.ledger) and T.owner and getOpenedProcessID()~=T.owner.pid then
    return false,'Reattach Cheat Engine to the original Curse of the Moon process, then use Clear All.'
  end
  if getOpenedProcessID()~=pid then openProcess(pid) end
  local m=module(pid)
  if not m then return false,'The game cannot be read. Run Cheat Engine with the same access level as the game, then try again.' end
  local ok,hash=pcall(md5file,m.PathToFile)
  if not ok or not hash then return false,'The game executable could not be checked. Make sure its installation folder is accessible.' end
  if hash:lower()~='951389c953020fc7b5def32e7bed129a' then
    return false,'This table supports Steam build 2824730. Your executable is different and needs a matching table.'
  end
  local peOffset=readInteger(m.Address+0x3c)
  if not peOffset or readInteger(m.Address+peOffset+8)~=0x5b10d580 then
    return false,'The running game does not match the supported build. Restart the game and try again.'
  end
  if T.owner and (T.owner.pid~=pid or T.owner.base~=m.Address) then T.resolved={};T.scratch=nil end
  T.owner={pid=pid,base=m.Address,stampAddress=m.Address+peOffset+8}
  T.lastError=nil
  return true
end
-- Identity is checked before every write, including rollback.
sameProcess=function()
  if not T.owner or getOpenedProcessID()~=T.owner.pid then return false end
  local m=module(T.owner.pid)
  return m and m.Address==T.owner.base and byte(m.Address)==0x4d
    and readInteger(T.owner.stampAddress)==0x5b10d580
end
local function restoreBody(group)
  if not next(T.ledger) then return true end
  if T.owner and alive(T.owner.pid)==false then T.ledger={}; T.resolved={}; return true end
  if not sameProcess() then
    notify('The original game is not attached. Reattach to Curse of the Moon and use Clear All to restore its options.')
    return false
  end
  local success=true
  for i=#patches,1,-1 do
    local p=patches[i]; local address=T.ledger[i]
    if address and (not group or p.group==group) then
      local currentOriginal=equal(address,p.old)
      if currentOriginal then T.ledger[i]=nil
      elseif ownedBytes(address,p) and sameProcess() and write(address,p.old) then T.ledger[i]=nil
      else success=false end
    end
  end
  if not success then notify('An option could not be restored because the game code changed or became unreadable. Keep this table open, reattach to the game, and try Clear All again.') end
  return success
end
-- Only multi-byte edits pause the target; idle checks never suspend it.
local function transaction(group,restoring,body)
  local needsPause=false
  for i,p in ipairs(patches) do
    if (not group or p.group==group) and #p.old~=1
       and (not restoring or T.ledger[i]) then needsPause=true end
  end
  if not needsPause then return body(group) end
  if not sameProcess() then
    notify('The original game is not attached. Reattach to Curse of the Moon and use Clear All to restore its options.')
    return false
  end
  local paused=pcall(pause)
  if not paused then notify('The game could not be paused briefly to change this option safely. Try again.'); return false end
  local ok,result=pcall(body,group)
  if not ok and not restoring then pcall(restoreBody,group) end
  local resumed=pcall(unpause)
  if not resumed then notify('The game could not be resumed. Use Cheat Engine to resume the attached game.') end
  if not ok then notify('The option could not be changed completely. Use Clear All before trying again.'); return false end
  return result and resumed
end
local function restore(group)
  if not next(T.ledger) then return true end
  if T.owner and alive(T.owner.pid)==false then T.ledger={}; T.resolved={}; return true end
  return transaction(group,true,restoreBody)
end
local function enable(group)
  local valid,why=validate()
  if not valid then notify(why); return false end
  if not rec(10).Active then notify('Enable MAIN before turning on a cheat.'); return false end
  for i,p in ipairs(patches) do
    if p.group==group then
      local address=T.resolved[i]
      if not address then
        local ok,found=pcall(AOBScanModuleUnique,'COTM.exe',p.pattern)
        if not ok or not found then
          notify('The required game code could not be found uniquely. Disable other trainers and restart the game before trying again.')
          return false
        end
        address=found+p.offset; T.resolved[i]=address
      end
      if not equal(address,p.old) and not (T.ledger[i]==address and equal(address,p.new)) then
        notify('Another modification is using the same game code. Disable it or restart the game before enabling this option.')
        return false
      end
    end
  end
  return transaction(group,false,function()
  for i,p in ipairs(patches) do
    if p.group==group and not T.ledger[i] then
      local address=T.resolved[i]
      if not sameProcess() or not equal(address,p.old) then restoreBody(group); return false end
      T.ledger[i]=address
      if not write(address,p.new) then
        restoreBody(group)
        notify('The cheat could not be applied completely. Use Clear All and restart the game before trying again.')
        return false
      end
    end
  end
  return true
  end)
end

local weaponPattern='8B 81 98 01 00 00 8B 0D ?? ?? ?? ?? 83 C0 06 8D 14 40 8B 41 08 8A 04 02 C3'
local weaponNames={
  [17]={'Zangetsu','Chain','Talisman','Demon Essence'},
  [18]={'Miriam','Dagger','Rapier','Axe','Scythe'},
  [19]={'Alfred','Burning Sphere','Frostcalibur','Soul Vision','Voltaic Ray'}
}
local function pointer(a)
  local v=readInteger(a)
  if not v or v==0 then return nil end
  return v
end
local function weaponState(global)
  if not sameProcess() then return nil end
  local manager=pointer(global)
  if not manager or readInteger(manager)~=T.owner.base+0x3cce90 then return nil end
  local save=pointer(manager+8)
  if not save or readInteger(save)~=T.owner.base+0x3ba38c then return nil end
  return save,manager
end
local function equip(lock)
  if not T.owner or getOpenedProcessID()~=T.owner.pid or alive(T.owner.pid)==false then return false end
  if not sameProcess() then return nil end
  local save,manager=weaponState(lock.global)
  if not save then return lock.established==true end
  -- Stage loading can replace both objects. Accept only the freshly validated pair.
  lock.manager=manager
  lock.save=save
  local address=save+0x12+lock.character*3
  local previous=byte(address)
  if previous==nil or not ({[0]=true,[1]=true,[2]=true,[3]=true,[4]=true})[previous] then return lock.established==true end
  if previous==lock.value then return true end
  if not pcall(pause) then return false end
  local ok,result=pcall(function()
    local current,owner=weaponState(lock.global)
    if not sameProcess() then return false end
    if current~=save then return lock.established==true end
    if owner~=manager then return lock.established==true end
    return write(address,{lock.value})
  end)
  local resumed=pcall(unpause)
  if not resumed then notify('The game could not be resumed. Use Cheat Engine to resume the attached game.') end
  return ok and result and resumed
end
local refreshWeapons
local function pickWeapon(id)
  if not rec(10).Active then notify('Enable MAIN first.'); return false end
  local valid,why=validate()
  if not valid then notify(why); return false end
  local found=AOBScanModuleUnique('COTM.exe',weaponPattern)
  if not found then notify('The subweapon selector could not find the game code. Restart the game and reopen this table.'); return false end
  local global=readInteger(found+8)
  local save,manager=weaponState(global)
  if not save then notify('The current game data is unavailable. Load your run, then try the picker again.'); return false end
  local names=weaponNames[id]
  local choice=T.scratch and byte(T.scratch+id-17)
  if not choice or choice==0 or not names[choice+1] then notify('Choose a weapon from the dropdown first.'); return false end
  local lock={global=global,save=save,manager=manager,character=id-17,value=choice}
  if not equip(lock) then notify('The game changed while choosing a weapon, or the weapon could not be equipped. Wait for your run to finish loading and try again.'); return false end
  lock.established=true
  T.locks[id]=lock

  return true
end
local function stopWeapons()
  T.locks={}
  T.busy=true
  for id in pairs(weaponNames) do
    local row=rec(id)
    if row then row.Active=false;row.Address='0' end
  end
  local picker=rec(20);if picker then picker.Active=false end
  T.busy=false
end

local function uncheck()
  stopWeapons()
  T.busy=true
  for _,id in ipairs({11,12,13,14,15,16,10}) do local r=rec(id); if r then r.Active=false end end
  T.busy=false
end
function T.clear()
  if not restore() then return false end
  uncheck(); T.lastError=nil; return true
end
function T.shutdown()
  if not T.clear() then return false end
  if T.dropdownTimer then T.dropdownTimer.destroy(); T.dropdownTimer=nil end
  if T.valueChange and records.OnValueChange==T.valueChange then records.OnValueChange=T.previousValueChange end
  if T.weaponTimer then T.weaponTimer.destroy(); T.weaponTimer=nil end
  if T.timer then T.timer.destroy(); T.timer=nil end
  if T.closeCallback then getMainForm().unregisterCloseCallback(T.closeCallback); T.closeCallback=nil end
  return true
end
local help=[[Table identification: Katzy.

Open the game, allow this table's Lua script, and tick MAIN. Choose each cheat separately.

Fuel the Cheats - Tip Katzy: toggle to open my Ko-fi page in your browser for optional tips. The entry unticks itself.

Infinite Lives: prevents deaths from consuming lives, including in Veteran Style. Extra lives can still be earned. Switch off to resume normal life loss; it does not revive a character who is already dead.

Invincibility: ignores ordinary enemy hits and their knockback. Pits and scripted deaths can still kill you. It works independently of Unlimited Health. Switch off to restore normal hit detection and any remaining natural immunity.

Unlimited Health: prevents ordinary damage from reducing the current character's health. It does not refill lost health or prevent pits and scripted deaths. Switch it off to restore normal damage.

Infinite Jump: press Jump again in midair to jump repeatedly with any character. Normal jump height and other action restrictions still apply. Switch off to restore normal jump limits.

Always Charged Swings - Zangetsu: each normal attack-button press uses his charged slash without waiting to charge or unlocking that move. This affects Zangetsu only and leaves subweapons unchanged. Switch off to restore normal attacks.

Unlimited Weapon Points: makes the game treat weapon points as full and blocks weapon-point spending, including special abilities. Pickups can refill the underlying point total. Switch it off to show the stored total and restore normal spending.

Subweapon Picker: enable the group to show the character rows. Double-click the value beside a name to select and equip a subweapon. Tick the character name to lock its weapon; changing its dropdown updates the lock. Untick the name to allow pickups and follow the equipped weapon. Unlocking leaves the weapon equipped. Closing Subweapon Picker stops all weapon locks. Gebel has no alternate subweapons. Locks wait through loading and reapply your selection when game data is available again. Switch them off before starting a different run if you do not want to keep those choices. Restarting the game clears the locks.

Clear All: stops weapon locks without restoring previous weapons, switches off all other cheats and MAIN, restoring normal behavior. Turning MAIN off does the same. Clear All before manually changing Cheat Engine's attached process.

After restarting the game, enable MAIN and your chosen options again.]]
for id,group in pairs({[11]='health',[12]='points',[13]='jump',[14]='lives',[15]='charge',[16]='invincible'}) do
  local r=rec(id)
  r.OnActivate=function(_,before)
    if not before or T.busy then return true end
    local ok,result=pcall(enable,group)
    if not ok then restore(group); notify('This option could not be enabled. Use Clear All, then reopen the table and try again.'); return false end
    return result
  end
  r.OnDeactivate=function(_,before) if not before or T.busy then return true end return restore(group) end
end
rec(10).OnActivate=function(_,before)
  if not before or T.busy then return true end
  local ok,result,why=pcall(validate)
  if not ok then notify('The game could not be checked. Reopen the table and try again.'); return false end
  if not result then notify(why) end
  return result
end
rec(10).OnDeactivate=function(_,before)
  if not before or T.busy then return true end
  if not restore() then return false end
  stopWeapons()
  T.busy=true; for _,id in ipairs({11,12,13,14,15,16}) do rec(id).Active=false end; T.busy=false
  return true
end
rec(1).OnActivate=function(_,before) if before then local ok,why=validate(); if not ok then notify(why) end; return false end return true end
rec(2).OnActivate=function(_,before) if before then showMessage(help); return false end return true end
rec(4).OnActivate=function(_,before)
  if before then
    local ok=pcall(shellExecute,'https://ko-fi.com/katzy')
    if not ok then showMessage('Your browser could not be opened. Visit https://ko-fi.com/katzy to tip Katzy.') end
    return false
  end
  return true
end
rec(3).OnActivate=function(_,before) if before then T.clear(); return false end return true end
rec(10).OnDestroy=function() T.shutdown() end
T.closeCallback=getMainForm().registerCloseCallback(function(_,action)
  if not T.clear() then return 0 end
  return action
end)
T.timer=createTimer(nil,false)
T.timer.Interval=1500
T.timer.OnTimer=function()
  local ok=pcall(function()
    if T.owner and alive(T.owner.pid)==false then
      T.ledger={}; T.resolved={}; T.owner=nil;T.scratch=nil; uncheck()
    end
    if next(T.ledger) then return end
    local pid=getProcessIDFromProcessName('COTM.exe')
    if pid and pid~=0 and (not T.owner or T.owner.pid~=pid) then
      local good,why=validate(); if not good then notify(why) end
    end
  end)
  if not ok then notify('Automatic attachment could not finish. Enable MAIN to retry the game check.') end
end
T.timer.Enabled=true
T.timer.OnTimer()


rec(20).OnActivate=function(_,before)
  if not before or T.busy then return true end
  if not rec(10).Active then notify('Enable MAIN first.');return false end
  local ok,why=validate();if not ok then notify(why);return false end
  return refreshWeapons(true)
end
rec(20).OnDeactivate=function(_,before) if before and not T.busy then stopWeapons() end;return true end
for id in pairs(weaponNames) do
  rec(id).OnActivate=function(_,before)
    if not before or T.busy then return true end
    if not rec(20).Active then notify('Enable Subweapon Picker first.');return false end
    local ok,result=pcall(pickWeapon,id)
    if not ok then notify('The subweapon picker could not finish. Wait for your run to finish loading and try again.'); return false end
    return result
  end
  rec(id).OnDeactivate=function(_,before)
    if before then T.locks[id]=nil end
    return true
  end
end
T.weaponTimer=createTimer(nil,false)
T.weaponTimer.Interval=50
T.weaponTimer.OnTimer=function()
  for id,lock in pairs(T.locks) do
    local ok,result=pcall(equip,lock)
    -- A failed read/write during loading is temporary; retain the user's choice.
    -- Only a confirmed loss of the attached game cancels an established lock.
    if ok and result==false and (not T.owner or getOpenedProcessID()~=T.owner.pid or alive(T.owner.pid)==false) then
      T.locks[id]=nil
      T.busy=true;rec(id).Active=false;T.busy=false
    end
  end
end
T.weaponTimer.Enabled=true

local previousValueChange=records.OnValueChange
T.previousValueChange=previousValueChange
refreshWeapons=function(opening)
  if not rec(10).Active or not (opening or rec(20).Active) or not sameProcess() then return false end
  local save=weaponState(T.owner.base+0x483660)
  if not save then return false end
  if not T.scratch then
    T.scratch=allocateMemory(4096,nil,0x04)
    if not T.scratch then return false end
  end
  for id,names in pairs(weaponNames) do
    local row=rec(id)
    local display=T.scratch+id-17
    if not T.locks[id] then
      local value=byte(save+0x12+(id-17)*3)
      if value and value&gt;=0 and value&lt;#names and byte(display)~=value then
        if not sameProcess() or not write(display,{value}) then return false end
      end
    end
    row.Address=string.format('%X',display)
  end
  return true
end

T.valueChange=function(list,r)
  if weaponNames[r.ID] then
    if not refreshWeapons() then notify('Enable MAIN and Subweapon Picker, then load your run before choosing a weapon.'); return true end
    return false
  end
  if previousValueChange then return previousValueChange(list,r) end
  return false
end
records.OnValueChange=T.valueChange
for id,names in pairs(weaponNames) do
  local r=rec(id)
  r.OnValueChangedByUser=function(_,_,newvalue)
    local value=tonumber(newvalue)
    if not value or value&lt;1 or value%1~=0 or not names[value+1] or not sameProcess() then return end
    local lock={global=T.owner.base+0x483660,character=id-17,value=value}
    if equip(lock) then
      if T.locks[id] then lock.established=true;T.locks[id]=lock end
    else
      notify('The weapon could not be equipped. Load your run and select it again.')
    end
  end
end
T.dropdownTimer=createTimer(nil,false)
T.dropdownTimer.Interval=500
T.dropdownTimer.OnTimer=function() pcall(refreshWeapons) end
T.dropdownTimer.Enabled=true
refreshWeapons()
</LuaScript>
</CheatTable>