<?xml version='1.0' encoding='utf-8'?>
<CheatTable CheatEngineTableVersion="45">
  <CheatEntries>
    <CheatEntry>
      <ID>0</ID>
      <Description>"Auto-Attach and Version Check"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>15</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
local record = memrec
shellExecute('https://ko-fi.com/katzy')
if record then
  createTimer(100, function() record.Active = false end)
end
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>1</ID>
      <Description>"How to use - Katzy"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
showMessage([==[Table identification: Katzy.

Start Broforce and open this table in Cheat Engine. Allow its Lua script, then tick MAIN. Choose each option separately.

Invincibility: protects player bros from ordinary damage and hazards that respect the game's invulnerability. Falling out of the map and scripted deaths can still kill. Switching off restores the previous protection setting.

Infinite Jump: press and release Jump repeatedly to jump again in midair. Normal jump height is retained. Switching off restores normal jump eligibility.

Infinite Bombs: prevents your bro's normal bomb/special charges from decreasing. Empty stock receives one temporary charge. Normal refills still work. Switching off restores normal spending and removes an unused temporary charge; genuine refills are retained. Each bro keeps their own special attack. Separate pocketed items and unique character meters are not covered.

Character Select and Lock (Player 1): enter a normal offline campaign level with Player 1 alive, enable this option, then double-click the Character value and choose a bro. The change happens immediately while the game is running and remains locked through rescues and respawns. Locked bros do not appear in the selector. Only bros you have unlocked are available. Newly unlocked bros appear automatically while this option is active. Switching off releases the lock immediately, even while paused, and leaves your current bro in place. Clear All does the same. Your next rescue or respawn follows normal character selection. The normal character-swap routine equips that bro with their usual starting abilities and specials. Not for online, versus, or Ironbro play.

Clear All or unticking MAIN disables all options. Options also clear when replacing this table or closing Cheat Engine normally. Use Clear All before manually attaching Cheat Engine to a different game.
]==])
local t=createTimer(nil)
t.Interval=100
t.OnTimer=function() t.destroy();memrec.Active=false end
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>2</ID>
      <Description>"Clear All"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
if BF then BF.guard(BF.clear) else showMessage('Allow the table Lua script, then reopen this table.') end
local t=createTimer(nil)
t.Interval=100
t.OnTimer=function() t.destroy();memrec.Active=false end
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>3</ID>
      <Description>"[MAIN] Broforce (build 12964083) v1.4"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
[DISABLE]
</AssemblerScript>
      <Options moHideChildren="1" />
      <CheatEntries>
        <CheatEntry>
          <ID>10</ID>
          <Description>"Invincibility"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>11</ID>
          <Description>"Infinite Jump"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>12</ID>
          <Description>"Infinite Bombs"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>13</ID>
          <Description>"Character Select and Lock (Player 1)"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
[DISABLE]
</AssemblerScript>
          <Options moHideChildren="1" />
          <CheatEntries>
            <CheatEntry>
              <ID>14</ID>
              <Description>"Character (double-click Value)"</Description>
              <DropDownList ReadOnly="1" DescriptionOnly="1" DisplayValueAsItem="1" />
              <VariableType>4 Bytes</VariableType>
              <Address>0</Address>
            </CheatEntry>
          </CheatEntries>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
  </CheatEntries>
  <UserdefinedSymbols />
  <LuaScript>--[[
Table identification: Katzy
Broforce | Steam build 12964083 | table v1.4
Original derivation from this installation's managed IL and live Mono code.
]]
if BF then BF.shutdown() end
BF={active={},patches={},cache={},diagnostics={},bombUI={}}
local M=BF
local ids={invincible=10,jump=11,bombs=12}
local specs={
 damage={method='Damage',original='55 48 8B EC 53 56 57 41 54 41 55 41 56 41 57'},
 jump={method='CanJumpOffGround',original='55 48 8B EC 56 41 56 41 57 48 81 EC D8 00 00 00'},
 bombs={method='set_SpecialAmmo',original='55 48 8B EC 57 48 83 EC 18 48 8B F9 48 89 55 F0'}
}
local function check(v,s) if not v then error(s,0) end return v end
local function hex(s) local t={} for b in s:gmatch('%x%x') do t[#t+1]=tonumber(b,16) end return t end
local function bytes(s) return {s:byte(1,#s)} end
local function append(a,b) for _,v in ipairs(b) do a[#a+1]=v end end
local function same(a,b)
 if not a or #a~=#b then return false end
 for i,v in ipairs(b) do if a[i]~=v then return false end end
 return true
end
local function read(a,n) return check(readBytes(a,n,true),'Broforce memory could not be read. Return to the game and try again.') end
local function put(a,b)
 check(writeBytes(a,b),'The change could not be written to Broforce. Close other trainers and try again.')
 check(same(read(a,#b),b),'Broforce did not retain the change. Close other trainers and try again.')
end
local function expect(a,b)
 check(same(read(a,#b),b),'Another modification or an unsupported game version was found. Restart Broforce without other trainers.')
end
local function jump(a) local b=hex('FF 25 00 00 00 00');append(b,bytes(string.pack('&lt;I8',a)));return b end
local function integer(a,n) return string.unpack(n==8 and '&lt;I8' or '&lt;i4',string.char(table.unpack(read(a,n)))) end
local function record(id) return getAddressList().getMemoryRecordByID(id) end
local function uncheck()
 M.syncing=true
 for _,id in ipairs({10,11,12,13,3}) do local r=record(id);if r then r.Active=false end end
 M.syncing=false
end
local function module(pid)
 for _,v in ipairs(enumModules(pid) or {}) do
  if v.Name:lower()=='broforce_beta.exe' then return v end
 end
end
function M.sameContext()
 if not M.pid or getOpenedProcessID()~=M.pid then return false end
 local processes=getProcesslist()
 if not processes or not processes[M.pid] or processes[M.pid]:lower()~='broforce_beta.exe' then return false end
 local v=module(M.pid)
 if not v or v.Address~=M.base or v.PathToFile:lower()~=M.path:lower() then return false end
 local age=getProcessAge()
 if age+2000&lt;M.age then return false end
 M.age=math.max(M.age,age)
 return same(readBytes(M.base,64,true),M.header)
end
local function transaction(f)
 check(M.sameContext(),'The Broforce connection changed. Reattach to the original game before clearing active options.')
 local alreadyPaused=isPaused()
 if not alreadyPaused then pause() end
 local ok,err=pcall(function()
  check(M.sameContext(),'Broforce restarted during the change. Re-enable MAIN for the new game.')
  f()
 end)
 if not alreadyPaused then unpause() end
 if not ok then error(err,0) end
end
local function field(cls,name,offset,kind)
 for _,f in ipairs(mono_class_enumFields(cls) or {}) do
  if f.name==name then
   check(f.offset==offset and f.typename==kind,'This Broforce build has a different layout. Use a table made for your installed build.')
   return f
  end
 end
 error('A required Broforce feature was not found. Use the unmodified Steam build 12964083.',0)
end
function M.prepare()
 M.forgetExited()
 if M.ready and M.sameContext() then return end
 check(not next(M.patches) and not M.god,'Clear the previous game connection before enabling MAIN again.')
 local pid=check(getProcessIDFromProcessName('Broforce_beta.exe'),'Broforce is not running. Start the game, then enable MAIN.')
 if getOpenedProcessID()~=pid then openProcess(pid) end
 check(getOpenedProcessID()==pid,'Cheat Engine could not connect to Broforce. Start both with matching permissions.')
 local v=check(module(pid),'The Broforce program could not be located. Wait for the game to finish starting.')
 check(v.Is64Bit,'This table requires the 64-bit Steam release of Broforce.')
 local folder=check(v.PathToFile:match('^(.*)[/\\][^/\\]+$'),'The Broforce installation folder could not be located.')
 local _,version=getFileVersion(v.PathToFile)
 check(version and version.major==2017 and version.minor==4 and version.release==7,'This Broforce executable has an unsupported version. Use Steam build 12964083.')
 check(md5file(v.PathToFile)=='78a6220348709e7f9bf5119f7f3d1795' and
       md5file(folder..'\\Broforce_beta_Data\\Managed\\Assembly-CSharp.dll')=='0b09140b48656ebe5e4a30e082271c48',
       'Wrong Broforce version: this table supports Steam build 12964083 with unmodified game files.')
 check(LaunchMonoDataCollector()~=0,'The game connection is not ready. Wait for the main menu, then enable MAIN again.')
 local c=check(mono_findClass('','TestVanDammeAnim'),'Broforce character data is not ready. Wait for the main menu.')
 local n=check(mono_findClass('','NetworkedUnit'),'Broforce player data is not ready. Wait for the main menu.')
 local h=check(mono_findClass('','HeroController'),'Broforce player data is not ready. Wait for the main menu.')
 local player=check(mono_findClass('','Player'),'Broforce player data is not ready. Wait for the main menu.')
 field(c,'_specialAmmo',0x5DC,'System.Int32')
 field(c,'originalSpecialAmmo',0x5E0,'System.Int32');field(c,'heroType',0x494,'HeroType')
 field(c,'player',0x218,'Player');field(player,'_character',0xA8,'TestVanDammeAnim');field(player,'playerNum',0xF8,'System.Int32')
 field(h,'players',0,'Player[]')
 field(h,'ammoCounts',8,'System.Int32[]');field(h,'lastAmmoCounts',16,'System.Int32[]')
 field(n,'isHero',0x104,'System.Boolean');field(n,'_playerNum',0x100,'System.Int32')
 local f=field(h,'forceInvulnerability',0xB5,'System.Boolean')
 check(f.isStatic,'The Broforce protection setting has an unsupported layout.')
 local base=check(mono_class_getStaticFieldAddress(h),'Broforce protection is not ready. Enter a level, then try again.')
 check(base~=0,'Broforce protection is not ready. Enter a level, then try again.')
 M.pid=pid;M.base=v.Address;M.path=v.PathToFile;M.age=getProcessAge();M.header=read(M.base,64)
 M.godAddress=base+f.offset;M.playersAddress=base;M.ready=true;M.warned=nil
end
local function makeCode(which,original,address,cave)
 -- Unsigned player number &gt;3 also rejects negative enemy player numbers.
 local b=hex('80 B9 04 01 00 00 00 74 00 83 B9 00 01 00 00 03 77 00')
 local branches={9,18}
 if which=='jump' then append(b,hex('B8 01 00 00 00 C3'))
 elseif which=='damage' then
  append(b,hex('85 D2 7E 00 C3'));branches[#branches+1]=22
 elseif which=='bombs' then
  -- The Brolander stores power levels here rather than throwable specials.
  append(b,hex('83 B9 94 04 00 00 29 74 00'));branches[#branches+1]=#b
  append(b,hex('83 B9 E0 05 00 00 00 7E 00'));branches[#branches+1]=#b
  -- A real refill clears ownership of the temporary charge, even if its
  -- amount equals one. Decrements keep the charge and its ownership intact.
  append(b,hex('3B 91 DC 05 00 00 7C 00'));local decrease=#b
  append(b,hex('8B 81 00 01 00 00 48 C1 E0 04 49 BB'))
  append(b,bytes(string.pack('&lt;I8',cave+128)))
  append(b,hex('49 01 C3 49 39 0B 75 00'));branches[#branches+1]=#b
  append(b,hex('41 C7 43 08 00 00 00 00 EB 00'));branches[#branches+1]=#b
  b[decrease]=#b-decrease
  append(b,hex('8B 91 DC 05 00 00'))
 end
 local firstOriginal=#b+1
 for _,i in ipairs(branches) do b[i]=firstOriginal-i-1 end
 append(b,original);append(b,jump(address+#original))
 return b
end
function M.plan(which)
 if M.cache[which] then return M.cache[which] end
 local s=specs[which]
 local method=check(mono_findMethod('','TestVanDammeAnim',s.method),'The requested Broforce feature could not be found. Restart the game and try again.')
 check(method~=0,'The requested Broforce feature could not be found. Restart the game and try again.')
 local a=check(mono_compile_method(method),'The requested Broforce feature is not ready. Enter a level, then try again.')
 check(a~=0,'The requested Broforce feature is not ready. Enter a level, then try again.')
 local j=check(mono_getJitInfo(a),'The game code could not be verified. Restart Broforce and try again.')
 local original=hex(s.original)
 check(j.code_start==a and j.method==method and j.code_size&gt;=#original,'Broforce returned unexpected game code. Restart the game without other trainers.')
 expect(a,original)
 local cave=check(allocateMemory(256),'Cheat Engine could not reserve memory for this option. Restart the game and try again.')
 check(cave~=0,'Cheat Engine could not reserve memory for this option. Restart the game and try again.')
 local code=makeCode(which,original,a,cave)
 check(#code&lt;=128,'The requested option could not be prepared. Reopen the table and try again.')
 put(cave,code)
 if which=='bombs' then put(cave+128,bytes(string.rep('\0',64))) end
 local patch=jump(cave);while #patch&lt;#original do patch[#patch+1]=0x90 end
 local p={address=a,original=original,patch=patch,cave=cave,code=code}
 M.cache[which]=p;return p
end
function M.characters()
 local a=integer(M.playersAddress,8)
 if a==0 then return {} end
 check(integer(a+0x18,4)==4,'Broforce player data changed. Return to a level and try again.')
 local found={}
 for i=0,3 do
  local player=integer(a+0x20+i*8,8)
  if player~=0 and integer(player+0xF8,4)==i then
   local c=integer(player+0xA8,8)
   if c~=0 and integer(c+0x100,4)==i and read(c+0x104,1)[1]==1 and integer(c+0x218,8)==player and integer(c+0xD4,4)&gt;0 then
    found[i]=c
   end
  end
 end
 return found
end
local function hud(i)
 local addresses={}
 for _,offset in ipairs({8,16}) do
  local a=integer(M.playersAddress+offset,8)
  check(a~=0 and integer(a+0x18,4)==4,'The bomb display is not ready. Enter a level and try again.')
  addresses[#addresses+1]=a+0x20+i*4
 end
 return addresses
end
function M.updateBombs()
 local p=M.patches.bombs;if not p then return end
 local function empty(c)
  return integer(c+0x494,4)~=41 and integer(c+0x5E0,4)&gt;0 and integer(c+0x5DC,4)==0
 end
 local pending=false
 for i,c in pairs(M.characters()) do if empty(c) then pending=true end end
 if not pending then return end
 transaction(function()
  for i,c in pairs(M.characters()) do
   if empty(c) then
    local slot=p.cave+128+i*16
    local display=hud(i)
    M.bombUI[i]={character=c,addresses=display,values={integer(display[1],4),integer(display[2],4)}}
    -- Ownership is committed before the only game-field write.
    put(slot,bytes(string.pack('&lt;I8I4I4',c,1,0)))
    put(c+0x5DC,bytes(string.pack('&lt;i4',1)))
    for _,address in ipairs(display) do put(address,bytes(string.pack('&lt;i4',1))) end
   end
  end
 end)
end
local function restoreBombs()
 local p=M.cache.bombs;if not p then return end
 local chars=M.characters()
 for i=0,3 do
  local slot=p.cave+128+i*16
  local c=integer(slot,8);local owned=integer(slot+8,4)
  if owned==1 and c~=0 and chars[i]==c and integer(c+0x5DC,4)==1 then
   put(c+0x5DC,bytes(string.pack('&lt;i4',0)))
  end
  local ui=M.bombUI[i]
  if owned==1 and ui and ui.character==c and chars[i]==c and integer(c+0x5DC,4)==0 then
   local current=hud(i)
   for j,address in ipairs(current) do
    if address==ui.addresses[j] then put(address,bytes(string.pack('&lt;i4',ui.values[j]))) end
   end
  end
  if c~=0 or owned~=0 then put(slot,bytes(string.rep('\0',16))) end
  M.bombUI[i]=nil
 end
end
local function install(which,p)
 expect(p.address,p.original);expect(p.cave,p.code)
 -- Record ownership before writing so even a partial write retains its cleanup data.
 M.patches[which]=p
 local ok,err=pcall(put,p.address,p.patch)
 if not ok then
  local restored=pcall(put,p.address,p.original)
  if restored then M.patches[which]=nil end
  error(err,0)
 end
end
local function restore(which)
 local p=M.patches[which];if not p then return end
 local cur=read(p.address,#p.original)
 if same(cur,p.original) then M.patches[which]=nil;return end
 check(same(cur,p.patch),'Another trainer changed an active option. Close that trainer before trying Clear All again.')
 put(p.address,p.original);M.patches[which]=nil
end
local function restoreGod()
 if not M.god then return end
 local g=M.god
 check(read(g.address,1)[1]&lt;=1,'Broforce protection changed unexpectedly. Restart the game to clear it.')
 put(g.address,{g.original});M.god=nil
end
function M.set(which,on)
 if not on then M.forgetExited() end
 if on then
  M.prepare()
  if M.active[which] then return end
  local p=M.plan(which=='invincible' and 'damage' or which)
  transaction(function()
   if which=='invincible' then
    local old=read(M.godAddress,1)[1];check(old&lt;=1,'Broforce protection is not ready. Enter a level and try again.')
    M.god={address=M.godAddress,original=old}
    local ok,err=pcall(function() put(M.godAddress,{1});install('damage',p) end)
    if not ok then pcall(restore,'damage');pcall(restoreGod);error(err,0) end
   else install(which,p) end
  end)
  M.active[which]=true
  if which=='bombs' then
   local ok,err=pcall(M.updateBombs)
   if not ok then pcall(M.set,'bombs',false);error(err,0) end
  end
 else
  local key=which=='invincible' and 'damage' or which
  if not M.patches[key] and not (which=='invincible' and M.god) then M.active[which]=nil;return end
  transaction(function()
   if which=='bombs' then restoreBombs() end
   restore(key);if which=='invincible' then restoreGod() end
  end)
  M.active[which]=nil
 end
 if M.timer then M.timer.Interval=(M.active.bombs or M.active.character) and 250 or 1500 end
end
local charPayload={
worker={code="53 51 48 83 ec 28 48 bb 01 00 00 00 11 11 11 11 83 7b 04 00 0f 84 60 01 00 00 83 7b 18 00 0f 85 56 01 00 00 48 b8 03 00 00 00 11 11 11 11 ff d0 85 c0 0f 84 35 01 00 00 48 b8 04 00 00 00 11 11 11 11 ff d0 85 c0 0f 85 21 01 00 00 48 b8 12 00 00 00 11 11 11 11 ff d0 85 c0 0f 85 0d 01 00 00 8b 03 83 f8 33 0f 87 e9 00 00 00 80 7c 03 40 01 0f 85 de 00 00 00 48 b8 02 00 00 00 11 11 11 11 48 8b 00 48 85 c0 0f 84 e1 00 00 00 83 78 18 04 0f 85 d7 00 00 00 48 8b 48 20 48 85 c9 0f 84 ca 00 00 00 83 b9 f8 00 00 00 00 0f 85 bd 00 00 00 48 8b 91 a8 00 00 00 48 85 d2 0f 84 ad 00 00 00 83 ba d4 00 00 00 00 0f 8e a0 00 00 00 83 ba 00 01 00 00 00 0f 85 93 00 00 00 80 ba 04 01 00 00 01 0f 85 86 00 00 00 48 39 8a 18 02 00 00 75 7d 8b 82 94 04 00 00 83 f8 33 77 72 3b 03 74 6e c7 43 18 01 00 00 00 31 c9 8b 53 08 48 b8 05 00 00 00 11 11 11 11 ff d0 c7 43 18 00 00 00 00 ff 43 14 48 b8 02 00 00 00 11 11 11 11 48 8b 00 48 85 c0 74 2a 48 8b 40 20 48 85 c0 74 21 48 8b 80 a8 00 00 00 48 85 c0 74 15 8b 80 94 04 00 00 3b 03 75 0b eb 19 c7 43 10 02 00 00 00 eb 07 c7 43 10 01 00 00 00 c7 43 04 00 00 00 00 eb 0d 83 7b 04 02 75 07 c7 43 04 00 00 00 00 48 83 c4 28 59 5b 55 48 89 e5 56 57 41 57 48 83 ec 08 48 89 ce 49 bb 06 00 00 00 11 11 11 11 41 ff e3",relocations={{8,"config"},{120,"players"},{291,"players"},{38,"offline"},{58,"mode"},{269,"change"},{401,"workerReturn"},{78,"hardcore"}}},
spawn={code="51 52 41 50 41 51 53 48 83 ec 20 48 bb 01 00 00 00 11 11 11 11 83 7b 04 00 74 76 83 7b 10 00 75 70 48 b8 03 00 00 00 11 11 11 11 ff d0 85 c0 74 60 48 b8 04 00 00 00 11 11 11 11 ff d0 85 c0 75 50 48 b8 12 00 00 00 11 11 11 11 ff d0 85 c0 75 40 48 8b 4c 24 40 83 b9 f8 00 00 00 00 75 32 83 7c 24 30 00 75 2b 48 b8 02 00 00 00 11 11 11 11 48 8b 00 48 85 c0 74 19 48 39 48 20 75 13 8b 03 83 f8 33 77 0c 80 7c 03 40 01 75 05 48 89 44 24 38 48 83 c4 20 5b 41 59 41 58 5a 59 55 48 89 e5 53 57 41 54 41 55 41 57 48 83 ec 58 49 bb 07 00 00 00 11 11 11 11 41 ff e3",relocations={{13,"config"},{104,"players"},{35,"offline"},{51,"mode"},{174,"spawnReturn"},{67,"hardcore"}}},
}
local charNames={[0]="Rambro",[1]="Brommando",[2]="B.A. Broracus",[3]="Brodell Walker",[4]="Brade",[5]="MacBrover",[6]="Bro In Black",[7]="Brobocop",[8]="Bro Dredd",[9]="Bro Hard",[10]="Bro Max",[11]="Snake Broskin",[12]="Brominator",[13]="Indiana Brones",[14]="Ash Brolliams",[15]="Mr Anderbro",[16]="Boondock Bros",[17]="Brochete",[18]="Bronan The Brobarian",[19]="Ellen Ripbro",[20]="Cherry Broling",[21]="Time Bro",[22]="Col. James Broddock",[23]="Broniversal Soldier",[24]="Broney Ross",[25]="Lee Broxmas",[26]="Bronnar Jensen",[27]="Bro Caesar",[28]="Trent Broser",[29]="Broctor Death",[30]="Toll Broad",[31]="The Brode",[32]="Bro Max",[33]="Double Bro Seven",[34]="The Brodator",[35]="The Brofessional",[36]="Brondle Fly",[37]="Bro Heart",[38]="The Brocketeer",[39]="Tank Bro",[40]="Broden",[41]="The Brolander",[42]="Bro Lee",[43]="Dirty Brody",[44]="Casey Broback",[45]="Xebro",[46]="The Scorpion Bro",[47]="Broffy the Vampire Slayer",[48]="Desperabro",[49]="Bro Gummer",[50]="Chev Brolios",[51]="Demolition Bro"}
-- Character actions run from HeroController's existing main-thread update.
local charSpecs={
 worker={'HeroController','UpdateDebugInputs','55 48 8B EC 56 57 41 57 48 83 EC 08 48 8B F1',15},
 spawn={'Player','InstantiateHero','55 48 8B EC 53 57 41 54 41 55 41 57 48 83 EC 58',16},
}
local function charMethod(cls,name)
 local method=check(mono_findMethod('',cls,name),'A character feature could not be found. Use the unmodified supported game build.')
 check(method~=0,'A character feature could not be found. Restart Broforce and try again.')
 local a=check(mono_compile_method(method),'Character selection is not ready. Enter a level and try again.')
 local j=check(mono_getJitInfo(a),'Character code could not be verified. Restart Broforce and try again.')
 check(j.method==method and j.code_start==a,'Character code changed unexpectedly. Restart Broforce without other trainers.')
 return a
end
function M.realHeroes()
 local p=integer(M.progressStatic,8)
 check(p~=0,'Your unlock progress is not ready. Enter a campaign level first.')
 local allowed={}
 for _,off in ipairs({16,24}) do
  local list=integer(p+off,8)
  check(list~=0,'Your unlock progress could not be read. Return to a campaign level.')
  local n=integer(list+24,4);local a=integer(list+16,8)
  check(n&gt;=0 and n&lt;=52 and a~=0 and integer(a+24,4)&gt;=n and integer(a+24,4)&lt;=256,'Your unlock list has an unsupported layout. Restart Broforce without mods.')
  for i=0,n-1 do local hero=integer(a+32+i*4,4);if hero&gt;=0 and hero&lt;=51 then allowed[hero]=true end end
 end
 check(next(allowed)~=nil,'No unlocked bro is ready yet. Start a campaign level first.')
 return allowed
end
local function firstHero(set)
 for i=0,51 do if set[i] then return i end end
 error('No unlocked bro is available. Start a campaign level first.',0)
end
function M.refreshHeroes()
 if not M.charConfig then return end
 local natural=M.realHeroes();local lines={};local mask={}
 for i=0,51 do
  mask[i+1]=natural[i] and 1 or 0
  if mask[i+1]==1 then lines[#lines+1]=i..':'..charNames[i] end
 end
 local signature=table.concat(lines,'\n')
 if signature~=M.charRoster then
  transaction(function() put(M.charConfig+64,mask) end)
  local r=record(14);if r then r.DropDownList.Text=signature end
  M.charRoster=signature
 end
 return natural
end
function M.prepareCharacters()
 M.prepare()
 if M.charConfig then return end
 local c=check(mono_findClass('','PlayerProgress'),'Your character progress is not ready. Enter a campaign level.')
 field(c,'instance',0,'PlayerProgress');field(c,'unlockedHeroes',16,'System.Collections.Generic.List&lt;HeroType&gt;')
 field(c,'yetToBePlayedUnlockedHeroes',24,'System.Collections.Generic.List&lt;HeroType&gt;')
 M.progressStatic=check(mono_class_getStaticFieldAddress(c),'Your character progress is not ready. Enter a campaign level.')
 local change=charMethod('HeroController','ChangeBro')
 local offline=charMethod('Connect','get_IsOffline')
 local mode=charMethod('GameModeController','get_GameMode')
 local hardcore=charMethod('GameModeController','get_IsHardcoreMode')
 local addresses,originals={},{}
 for key,s in pairs(charSpecs) do
  local a=charMethod(s[1],s[2]);expect(a,hex(s[3]));addresses[key]=a;originals[key]=read(a,s[4])
 end
 local page=check(allocateMemory(4096),'Could not prepare character selection. Restart Broforce and try again.')
 check(page~=0,'Could not prepare character selection. Restart Broforce and try again.')
 put(page,bytes(string.rep('\0',128)))
 local values={config=page,players=M.playersAddress,offline=offline,mode=mode,hardcore=hardcore,change=change,
  workerReturn=addresses.worker+15,spawnReturn=addresses.spawn+16}
 M.charPlans={};local index=0
 for _,key in ipairs({'worker','spawn'}) do
  index=index+1;local cave=page+index*512;local template=charPayload[key];local code=hex(template.code)
  for _,rel in ipairs(template.relocations) do
   local b=bytes(string.pack('&lt;I8',check(values[rel[2]],'Character data could not be prepared. Reopen the table.')))
   for j=1,8 do code[rel[1]+j]=b[j] end
  end
  put(cave,code);local patch=jump(cave);while #patch&lt;#originals[key] do patch[#patch+1]=0x90 end
  M.charPlans[key]={address=addresses[key],original=originals[key],patch=patch,cave=cave,code=code}
 end
 M.charConfig=page;M.charRoster=nil;M.refreshHeroes()
end
local function charAttach(keys)
 for _,key in ipairs(keys) do install('char_'..key,M.charPlans[key]) end
end
local function charDetach(keys)
 for _,key in ipairs(keys) do restore('char_'..key) end
end
local function clearCharacterState()
 M.active.character=nil;M.charErrorShown=nil;M.charRoster=nil
 local r=record(14);if r then r.Address='0';r.DropDownList.Text='' end
end
function M.character(on)
 if on then
  if M.active.character then return end
  M.prepareCharacters()
  local chars=M.characters();local current=check(chars[0],'Player 1 needs to be alive in an offline campaign level before enabling character selection.')
  local original=integer(current+0x494,4)
  check(original&gt;=0 and original&lt;=51,'Finish the current scripted sequence before enabling character selection.')
  local natural=M.refreshHeroes();local selected=natural[original] and original or firstHero(natural)
  transaction(function()
   put(M.charConfig,bytes(string.pack('&lt;i4i4i4i4i4i4i4',selected,0,natural[original] and original or firstHero(natural),0,0,0,0)))
   local ok,err=pcall(function() charAttach({'spawn','worker'});put(M.charConfig+4,bytes(string.pack('&lt;i4',1))) end)
   if not ok then pcall(charDetach,{'worker','spawn'});error(err,0) end
  end)
  M.active.character=true;local r=record(14);if r then r.Address=string.format('%X',M.charConfig) end
 else
  M.forgetExited()
  if M.patches.char_worker or M.patches.char_spawn then
   check(M.sameContext(),'Reattach to Broforce before disabling character selection.')
   transaction(function()
    -- Stop new swaps without waiting for the game update or changing the bro.
    put(M.charConfig+4,bytes(string.pack('&lt;i4',0)))
    charDetach({'worker','spawn'})
   end)
  end
  clearCharacterState()
 end
 if M.timer then M.timer.Interval=(M.active.character or M.active.bombs) and 250 or 1500 end
end
function M.characterTick()
 if not M.charConfig or not M.active.character then return end
 M.refreshHeroes()
 if M.active.character and integer(M.charConfig+16,4)~=0 and not M.charErrorShown then
  M.charErrorShown=true
  showMessage('The chosen bro could not be switched in this level. Choose another unlocked bro, or turn Character Select and Lock off and back on.')
 end
end

function M.clear()
 M.character(false)
 M.forgetExited()
 if next(M.patches) or M.god then
  transaction(function()
   restoreBombs();restore('bombs');restore('jump');restore('damage');restoreGod()
  end)
 end
 M.active={};uncheck()
 if M.timer then M.timer.Interval=1500 end
end
function M.forgetExited()
 if not M.pid then return false end
 local processes=getProcesslist()
 if processes and not processes[M.pid] then
  M.patches={};M.god=nil;M.active={};M.cache={};M.bombUI={};M.charConfig=nil;M.charPlans=nil;M.progressStatic=nil;clearCharacterState();M.pid=nil;M.ready=false;uncheck()
  return true
 end
 return false
end
function M.report(err)
 M.diagnostics[#M.diagnostics+1]=tostring(err)
 -- All our explicit failures are plain-language. Never expose a raw Lua error.
 local s=tostring(err)
 if s:find(':%d+:') or s:find('stack traceback',1,true) or s:find('attempt to ',1,true) then
  s='Cheat Engine could not complete the change. Use Clear All, restart Broforce, and try again.'
 end
 showMessage(s)
end
function M.guard(f)
 local ok,err=pcall(f)
 if not ok then M.report(err) end
 return ok
end
function M.bind()

 for id,action in pairs({[13]=M.character}) do
  local f=action;local r=record(id)
  if r then
   r.OnActivate=function(_,before)
    if M.syncing or not before then return true end
    if not record(3).Active then showMessage('Enable MAIN before choosing an option.');return false end
    return M.guard(function() f(true) end)
   end
   r.OnDeactivate=function(_,before)
    if M.syncing or not before then return true end
    return M.guard(function() f(false) end)
   end
  end
 end
 local selector=record(14)
 if selector then
  selector.OnActivate=function() return false end
  selector.OnValueChangedByUser=function()
   if not M.active.character or not M.charConfig then return end
   M.guard(function()
    local value=integer(M.charConfig,4)
    check(value&gt;=0 and value&lt;=51 and read(M.charConfig+64+value,1)[1]==1,'Choose an unlocked bro from the dropdown.')
    transaction(function()
     put(M.charConfig+16,bytes(string.pack('&lt;i4',0)));put(M.charConfig+4,bytes(string.pack('&lt;i4',1)))
    end)
    M.charErrorShown=nil
   end)
  end
 end
 for which,id in pairs(ids) do
  local key=which;local r=record(id)
  if r then
   r.OnActivate=function(_,before)
    if M.syncing or not before then return true end
    if not record(3).Active then showMessage('Enable MAIN before choosing an option.');return false end
    return M.guard(function() M.set(key,true) end)
   end
   r.OnDeactivate=function(_,before)
    if M.syncing or not before then return true end
    return M.guard(function() M.set(key,false) end)
   end
  end
 end
 local main=record(3)
 if main then
  main.OnActivate=function(_,before) if M.syncing or not before then return true end return M.guard(M.prepare) end
  main.OnDeactivate=function(_,before) if M.syncing or not before then return true end return M.guard(M.clear) end
 end
end
function M.tick()
 local pid=getProcessIDFromProcessName('Broforce_beta.exe')
 if M.pid then
  if not M.forgetExited() and not M.sameContext() then
   M.ready=false
   if not M.warned and (next(M.patches) or M.god) then
    M.warned=true;showMessage('The Broforce connection changed. Reattach to Broforce, then use Clear All before continuing.')
   end
   return
  end
 end
 if pid and not M.pid then
  openProcess(pid)
  local ok,err=pcall(M.prepare)
  if not ok and not M.attachWarning then M.attachWarning=true;M.report(err) end
 elseif not pid then M.attachWarning=nil end
 if M.active.bombs then
  local ok,err=pcall(M.updateBombs)
  if not ok then
   local cleared=pcall(M.set,'bombs',false)
   if cleared then local r=record(12);if r then r.Active=false end end
   error(err,0)
  end
 end
 M.characterTick()
 -- No game pauses or writes on idle timer ticks, including MAIN-only.
end
function M.shutdown()
 if M.timer then M.timer.Enabled=false end
 M.clear()
 if M.timer then M.timer.destroy();M.timer=nil end
 if onTableLoad==M.tableHandler then onTableLoad=M.oldTableHandler end
 local form=getMainForm()
 if form.OnCloseQuery==M.closeHandler then form.OnCloseQuery=M.oldCloseHandler end
 -- Retain tiny executable allocations until process exit: a paused thread may
 -- still be completing a hook when its entry bytes have been restored.
end
function M.start()
 M.bind()
 M.oldTableHandler=onTableLoad
 M.tableHandler=function(before)
  if before then M.shutdown() end
  if M.oldTableHandler then M.oldTableHandler(before) end
 end
 onTableLoad=M.tableHandler
 local form=getMainForm();M.oldCloseHandler=form.OnCloseQuery
 M.closeHandler=function(sender)
  if not M.guard(M.shutdown) then return false end
  if M.oldCloseHandler then return M.oldCloseHandler(sender) end
  return true
 end
 form.OnCloseQuery=M.closeHandler
 M.timer=createTimer(nil);M.timer.Interval=1500
 M.timer.OnTimer=function()
  local ok,err=pcall(M.tick)
  if not ok and not M.warned then M.warned=true;M.report(err) end
 end
 M.timer.OnTimer()
end
M.start()
</LuaScript>
</CheatTable>