Run it
Copy the script and paste it into your executor.
Players = :()
RunService = :()
UserInputService = :()
TweenService = :()
TeleportService = :()
HttpService = :()
GuiService = :()
StarterGui = :()
LocalPlayer = Players.LocalPlayer
PlayerGui = LocalPlayer:()
Camera = .CurrentCamera
isMobile = UserInputService.TouchEnabled UserInputService.KeyboardEnabled
isPC = isMobile
Exec = {}
Exec.(path)
readfile
ok, result = (readfile, path)
ok result
Exec.(path, data)
writefile
(writefile, path, data)
Exec.(path, data)
appendfile
(appendfile, path, data)
writefile
existing = Exec.(path)
(writefile, path, existing .. data)
Exec.(path)
isfile
ok, result = (isfile, path)
ok result
readfile
ok = (readfile, path)
ok
Exec.(path)
makefolder
isfolder
ok, exists = (isfolder, path)
(ok exists)
(makefolder, path)
(makefolder, path)
Exec.(path)
isfolder
ok, result = (isfolder, path)
ok result
Exec.(path)
listfiles
ok, result = (listfiles, path)
ok result
{}
Exec.()
mouse1click
(mouse1click)
Input Input.click
(Input.click)
Exec.(x, y)
mousemoveabs
(mousemoveabs, x, y)
mousemoverel
(mousemoverel, x, y)
Exec.(keycode)
keypress
(keypress, keycode)
Input Input.keypress
(Input.keypress, keycode)
Exec.(keycode)
keyrelease
(keyrelease, keycode)
Input Input.keyrelease
(Input.keyrelease, keycode)
Exec.(button)
button button:()
(()
button:()
)
Exec.(url, headers, body)
.request
ok, res = (.request, {
Url = url, Method = ,
Headers = headers, Body = body
})
ok res
ok, res = (, {
Url = url, Method = ,
Headers = headers, Body = body
})
ok res
.request
ok, res = (.request, {
Url = url, Method = ,
Headers = headers, Body = body
})
ok res
ok, res = (()
HttpService:({
Url = url, Method = ,
Headers = headers, Body = body
})
)
ok res
BASE_KNOWLEDGE = {
version = ,
pc = {
movement = {
forward = .KeyCode.W,
backward = .KeyCode.S,
left = .KeyCode.A,
right = .KeyCode.D,
jump = .KeyCode.Space,
sprint = .KeyCode.LeftShift,
},
camera = ,
},
mobile = {
movement = ,
jump = ,
camera = ,
},
commonUI = {
playButton = {, , , , },
exitButton = {, , , },
confirmButton= {, , , },
shopButton = {, , },
},
horrorDefaults = {
threatKeywords = {
, , , , ,
, , , , ,
, , , ,
},
safeObjects = {, , , },
runTriggers = {, , },
}
}
BRAIN_FOLDER =
CONFIG_FILE =
Brain = {}
BrainPath =
ActivePlace =
()
ok, id = (()
.GameId
)
ok (id) (.PlaceId)
()
(.PlaceId)
()
ok, name = (()
MarketplaceService:(.PlaceId).Name
)
ok name name
.. .PlaceId
()
BRAIN_FOLDER .. .. () ..
(placeName)
{
placeName = placeName ( .. ()),
sessions = {},
mentalMap = {
landmarks = {},
safeZones = {},
dangerZones = {},
itemSpawns = {},
exits = {},
},
confidence = {
movement = ,
ui = ,
sequences = ,
},
topUIButtons = {},
totalActions = ,
totalPlaytime = ,
lastUpdated = .(),
}
()
{
version = ,
universeId = (),
baseKnowledge = BASE_KNOWLEDGE,
places = {},
config = {
activeProvider = ,
apiKeys = { claude = , chatgpt = , gemini = },
activePersona = ,
keybinds = {
reclaim = ,
toggleObs = ,
toggleLend= ,
toggleGUI = ,
},
scheduleEnabled = ,
scheduleStartMin = ,
scheduleDuration = ,
stealthMode = ,
},
totalSessions = ,
createdAt = .(),
lastUpdated = .(),
}
()
Brain BrainPath BrainPath ==
Brain.lastUpdated = .()
ok, encoded = (HttpService.JSONEncode, HttpService, Brain)
ok
Exec.(BrainPath, encoded)
()
Exec.(BRAIN_FOLDER)
BrainPath = ()
ActivePlace = ()
Exec.(BrainPath)
raw = Exec.(BrainPath)
raw raw ~=
ok, decoded = (HttpService.JSONDecode, HttpService, raw)
ok decoded
Brain = decoded
Brain.baseKnowledge = BASE_KNOWLEDGE
Brain.places[ActivePlace]
Brain.places[ActivePlace] = ()
Brain = ()
Brain.places[ActivePlace] = ()
()
()
Brain Brain.places Brain.places[ActivePlace]
Brain.places[ActivePlace]
Config = {}
()
Exec.(CONFIG_FILE)
raw = Exec.(CONFIG_FILE)
raw raw ~=
ok, decoded = (HttpService.JSONDecode, HttpService, raw)
ok
Config = decoded
Config = {
apiKeys = { claude = , chatgpt = , gemini = },
activeProvider= ,
activePersona = ,
stealthMode = ,
keybinds = {
reclaim = ,
toggleObs = ,
toggleLend = ,
toggleGUI = ,
}
}
()
ok, encoded = (HttpService.JSONEncode, HttpService, Config)
ok
Exec.(CONFIG_FILE, encoded)
FPS =
LearnCooldown =
lastFPSUpdate =
frameCount =
(dt)
frameCount +=
lastFPSUpdate += dt
lastFPSUpdate >=
FPS = frameCount
frameCount =
lastFPSUpdate =
FPS >
LearnCooldown =
FPS >=
LearnCooldown =
FPS >=
LearnCooldown =
LearnCooldown =
AIState = {
mode = ,
isLearning = ,
isPaused = ,
isStealth = ,
persona = ,
currentSession = ,
sessionStart = ,
lastLearnTick = ,
lastSaveTick = ,
tickCounter = ,
llmProvider = ,
llmCooldown = ,
threatDetected = ,
threatCooldown = ,
reclaimPending = ,
scheduleTimer = ,
scheduleActive = ,
}
PERSONAS = {
Mirror = { aggressionBias = , grindBias = , explorationBias = },
Aggressive = { aggressionBias = , grindBias = , explorationBias = },
Passive = { aggressionBias = , grindBias = , explorationBias = },
}
Observer = {}
Observer.lastPosition = .(,,)
Observer.pendingBatch = {}
Observer.batchFlushInterval =
Observer.()
AIState.currentSession = {
id = .(),
placeId = (),
starred = ,
priority = ,
actions = {},
movements = {},
uiClicks = {},
sequences = {},
startTime = .(),
endTime = ,
notes = ,
}
AIState.sessionStart = .()
Observer.()
AIState.currentSession
AIState.currentSession.endTime = .()
place = ()
place
.(place.sessions, AIState.currentSession)
place.totalPlaytime += (.() - AIState.sessionStart)
Observer.(place)
()
AIState.currentSession =
Observer.(pos)
AIState.currentSession
delta = (pos - Observer.lastPosition).Magnitude
delta <
Observer.lastPosition = pos
entry = {
pos = { x = pos.X, y = pos.Y, z = pos.Z },
t = .() - AIState.sessionStart,
}
.(AIState.currentSession.movements, entry)
.(AIState.currentSession.actions, { = , data = entry })
Observer.(pos)
Observer.(buttonName, buttonPath)
AIState.currentSession
entry = {
name = buttonName,
path = buttonPath ,
t = .() - AIState.sessionStart,
}
.(AIState.currentSession.uiClicks, entry)
.(AIState.currentSession.actions, { = , data = entry })
place = ()
place
place.topUIButtons[buttonName] = (place.topUIButtons[buttonName] ) +
place.totalActions +=
Observer.(label)
AIState.currentSession
.(AIState.currentSession.sequences, {
label = label,
t = .() - AIState.sessionStart,
actionIndex = #AIState.currentSession.actions,
})
Observer.(place)
sessions = place.sessions
n = #sessions
n ==
totalMoves, totalUI, totalSeq = , ,
_, s (sessions)
totalMoves += #(s.movements {})
totalUI += #(s.uiClicks {})
totalSeq += #(s.sequences {})
moveCap = .(, .(.(totalMoves + ) * ))
uiCap = .(, .(.(totalUI + ) * ))
seqCap = .(, .(.(totalSeq + ) * ))
place.confidence.movement = moveCap
place.confidence.ui = uiCap
place.confidence.sequences = seqCap
Observer.(pos)
place = ()
place
AIState.tickCounter % ~=
map = place.mentalMap
posKey = .(pos.X/) .. .. .(pos.Z/)
map.landmarks[posKey]
map.landmarks[posKey] = {
pos = { x = pos.X, y = pos.Y, z = pos.Z },
tag = ,
visits= ,
}
map.landmarks[posKey].visits +=
map.landmarks[posKey].visits >
map.landmarks[posKey].tag =
Observer.(tag)
char = LocalPlayer.Character
char
hrp = char:()
hrp
pos = hrp.Position
place = ()
place
posKey = .(pos.X/) .. .. .(pos.Z/)
place.mentalMap.landmarks[posKey]
place.mentalMap.landmarks[posKey] = {
pos = { x = pos.X, y = pos.Y, z = pos.Z },
tag = tag, visits = ,
}
place.mentalMap.landmarks[posKey].tag = tag
ThreatDetector = {}
ThreatDetector.()
place = ()
keywords = BASE_KNOWLEDGE.horrorDefaults.threatKeywords
place place.customThreatKeywords
_, kw (place.customThreatKeywords)
.(keywords, kw)
_, obj (:())
name = obj.Name:()
_, kw (keywords)
name:(kw:())
, obj.Name
runningCount =
_, p (Players:())
p ~= LocalPlayer p.Character
hum = p.Character:()
hum hum.WalkSpeed >
runningCount +=
runningCount >=
,
,
ThreatDetector.(threatName)
AIState.threatDetected =
AIState.threatCooldown =
Observer.()
AIState.currentSession
.(AIState.currentSession.actions, {
= ,
data = { name = threatName, t = .() - AIState.sessionStart }
})
AIState.mode == AIState.mode ==
GhostController.()
GhostController = {}
GhostController.replayIndex =
GhostController.replaySession =
GhostController.inputDisabled =
GhostController.()
place = ()
place #place.sessions ==
best =
bestScore = -
_, s (place.sessions)
score = #(s.actions {}) * (s.priority )
s.starred score = score *
score > bestScore
bestScore = score
best = s
best
GhostController.ghostModel =
GhostController.()
GhostController.ghostModel
(() GhostController.ghostModel:() )
GhostController.ghostModel =
char = LocalPlayer.Character
char
ghost = char:()
ghost.Name =
_, v (ghost:())
v:() v:() v:()
v:()
_, part (ghost:())
part:()
part.Transparency =
part.CanCollide =
part.Anchored =
part.CastShadow =
part.Color = .(, , )
part.Material = .Material.Neon
part:() part:()
hrp = char:()
ghostHRP = ghost:()
hrp ghostHRP
ghostHRP.CFrame = hrp.CFrame * .(, , )
billboard = .()
billboard.Size = .(, , , )
billboard.StudsOffset = .(, , )
billboard.AlwaysOnTop =
billboard.Parent = ghostHRP ghost.PrimaryPart ghost:()
tag = .()
tag.Size = .(,,,)
tag.BackgroundTransparency =
tag.Text =
tag.TextColor3 = .(, , )
tag.Font = .Font.GothamBold
tag.TextSize =
tag.Parent = billboard
ghost.Parent =
GhostController.ghostModel = ghost
ghost
GhostController.()
GhostController.ghostModel
(() GhostController.ghostModel:() )
GhostController.ghostModel =
GhostController.()
GhostController.replaySession = GhostController.()
GhostController.replaySession
VoidGUI.()
GhostController.replayIndex =
AIState.mode =
GhostController.()
VoidGUI.()
GhostController.()
GhostController.replaySession = GhostController.()
GhostController.replaySession
VoidGUI.()
GhostController.replayIndex =
GhostController.inputDisabled =
AIState.mode =
isMobile
UserInputService.InputBegan:((input, processed)
GhostController.inputDisabled AIState.reclaimPending
)
GhostController.()
GhostController.inputDisabled =
AIState.mode =
AIState.reclaimPending =
GhostController.()
VoidGUI.()
.()
AIState.reclaimPending =
GhostController.()
GhostController.replaySession
actions = GhostController.replaySession.actions
actions #actions ==
GhostController.replayIndex > #actions
GhostController.replayIndex =
action = actions[GhostController.replayIndex]
GhostController.replayIndex +=
action.type ==
GhostController.(action.data)
action.type ==
GhostController.(action.data)
GhostController.(data)
targetPos = .(data.pos.x, data.pos.y, data.pos.z)
AIState.mode == GhostController.ghostModel
ghost = GhostController.ghostModel
ghostHRP = ghost:()
ghostHum = ghost:()
ghostHRP ghostHum
speed =
AIState.persona == speed =
AIState.persona == speed =
ghostHum.WalkSpeed = speed
ghostHum:(targetPos)
ghostHRP
ghostHRP.CFrame = .(targetPos)
char = LocalPlayer.Character
char
hrp = char:()
hum = char:()
hrp hum
AIState.persona ==
hum.WalkSpeed =
AIState.persona ==
hum.WalkSpeed =
hum.WalkSpeed =
hum:(targetPos)
GhostController.(data)
(parent, targetName)
_, child (parent:())
child:() child.Name == targetName
child
button = (PlayerGui, data.name)
button
Exec.(button)
GhostController.()
char = LocalPlayer.Character
char
hrp = char:()
hum = char:()
hrp hum
place = ()
place
nearest =
nearestDist = .huge
_, landmark (place.mentalMap.landmarks)
landmark.tag ==
lpos = .(landmark.pos.x, landmark.pos.y, landmark.pos.z)
dist = (hrp.Position - lpos).Magnitude
dist < nearestDist
nearestDist = dist
nearest = lpos
nearest
hum.WalkSpeed =
hum:(nearest)
hum.WalkSpeed =
runDir = hrp.CFrame.LookVector * -
hum:(hrp.Position + runDir * )
LLMController = {}
LLMController.isWaiting =
LLMController.callCooldown =
LLM_PROVIDERS = {
claude = {
url = ,
model = ,
buildHeaders = (key)
{
[] = ,
[] = key,
[] = ,
}
,
buildBody = (prompt)
HttpService:({
model = ,
max_tokens = ,
messages = {{ role = , content = prompt }}
})
,
parseResponse = (body)
ok, decoded = (HttpService.JSONDecode, HttpService, body)
ok decoded.content decoded.content[]
decoded.content[].text
,
},
chatgpt = {
url = ,
model = ,
buildHeaders = (key)
{
[] = ,
[] = .. key,
}
,
buildBody = (prompt)
HttpService:({
model = ,
messages = {{ role = , content = prompt }},
max_tokens = ,
})
,
parseResponse = (body)
ok, decoded = (HttpService.JSONDecode, HttpService, body)
ok decoded.choices decoded.choices[]
decoded.choices[].message.content
,
},
gemini = {
url = ,
model = ,
buildHeaders = (key)
{
[] = ,
[]= key,
}
,
buildBody = (prompt)
HttpService:({
contents = {{ parts = {{ text = prompt }} }}
})
,
parseResponse = (body)
ok, decoded = (HttpService.JSONDecode, HttpService, body)
ok decoded.candidates decoded.candidates[]
parts = decoded.candidates[].content.parts
parts parts[]
parts[].text
,
},
}
LLMController.()
place = ()
place ,
place.sessions #place.sessions ==
,
totalActions =
_, s (place.sessions)
totalActions += #(s.actions {})
totalActions ==
,
provider = Config.activeProvider
provider == ,
key = Config.apiKeys[provider]
key key ==
, .. provider
,
LLMController.(gameState)
place = ()
place
safeZones, dangerZones = {}, {}
_, lm (place.mentalMap.landmarks)
lm.tag ==
.(safeZones, .(, lm.pos.x, lm.pos.y, lm.pos.z))
lm.tag ==
.(dangerZones, .(, lm.pos.x, lm.pos.y, lm.pos.z))
topUI = {}
name, count (place.topUIButtons)
.(topUI, name .. .. count .. )
.(,
place.placeName,
#place.sessions,
place.totalActions,
.(safeZones, ),
.(dangerZones, ),
.(topUI, ),
place.confidence.movement,
place.confidence.ui,
place.confidence.sequences,
AIState.persona,
gameState.pos.x, gameState.pos.y, gameState.pos.z,
gameState.nearbyPlayers,
(gameState.threatDetected),
AIState.mode
)
LLMController.(responseText)
responseText
clean = responseText:(, ):(, ):()
ok, action = (HttpService.JSONDecode, HttpService, clean)
ok action action.action
action
LLMController.(action)
action
char = LocalPlayer.Character
char
hrp = char:()
hum = char:()
action.action ==
hrp hum
dir = action.direction
moveVec = .(,,)
dir == moveVec = hrp.CFrame.LookVector
dir == moveVec = hrp.CFrame.LookVector * -
dir == moveVec = hrp.CFrame.RightVector * -
dir == moveVec = hrp.CFrame.RightVector
dir ==
GhostController.()
hum:(hrp.Position + moveVec * )
action.action ==
hum hum.Jump =
action.action ==
hum hum.WalkSpeed =
action.action ==
GhostController.({ name = action.target })
action.action ==
_, obj (:())
_, safeObj (BASE_KNOWLEDGE.horrorDefaults.safeObjects)
obj.Name:(safeObj) obj:()
hum hum:(obj.Position)
action.action ==
GhostController.()
action.action ==
.(action.duration )
LLMController.()
LLMController.isWaiting
canUse, reason = LLMController.()
canUse
VoidGUI.( .. (reason ))
AIState.mode =
char = LocalPlayer.Character
hrp = char char:()
pos = hrp hrp.Position .(,,)
nearby =
_, p (Players:())
p ~= LocalPlayer p.Character
phrp = p.Character:()
phrp (phrp.Position - pos).Magnitude <
nearby +=
gameState = {
pos = { x = pos.X, y = pos.Y, z = pos.Z },
nearbyPlayers = nearby,
threatDetected = AIState.threatDetected,
}
prompt = LLMController.(gameState)
prompt
provider = Config.activeProvider
prov = LLM_PROVIDERS[provider]
prov
LLMController.isWaiting =
.(()
headers = prov.(Config.apiKeys[provider])
body = prov.(prompt)
response = Exec.(prov.url, headers, body)
response response.Body
text = prov.(response.Body)
action = LLMController.(text)
action
LLMController.(action)
GhostController.()
GhostController.()
LLMController.isWaiting =
)
()
AIState.isLearning AIState.currentSession
Observer.()
()
.()
newPlaceId = ()
ActivePlace = newPlaceId
Brain.places[ActivePlace]
Brain.places[ActivePlace] = ()
AIState.isLearning
Observer.()
VoidGUI.()
VoidGUI.()
(()
TeleportService.LocalPlayerArrivedFromTeleport:(()
()
)
)
UIHooks = {}
(parent)
_, obj (parent:())
obj:()
obj.MouseButton1Click:(()
AIState.isLearning AIState.currentSession
Observer.(obj.Name, obj:())
)
parent.DescendantAdded:((obj)
obj:()
obj.MouseButton1Click:(()
AIState.isLearning AIState.currentSession
Observer.(obj.Name, obj:())
)
)
.(()
.()
_, gui (PlayerGui:())
gui:() gui.Name ~=
(gui)
PlayerGui.ChildAdded:((gui)
gui:() gui.Name ~=
(gui)
)
)
Keybinds = {}
Keybinds.binds = {
reclaim = .KeyCode.F,
toggleObs = .KeyCode.G,
toggleLend = .KeyCode.H,
toggleGUI = .KeyCode.J,
}
KEY_MAP = {
F = .KeyCode.F, G = .KeyCode.G, H = .KeyCode.H,
J = .KeyCode.J, K = .KeyCode.K, L = .KeyCode.L,
Z = .KeyCode.Z, X = .KeyCode.X, C = .KeyCode.C,
V = .KeyCode.V, B = .KeyCode.B, N = .KeyCode.N,
M = .KeyCode.M, R = .KeyCode.R, T = .KeyCode.T,
Y = .KeyCode.Y, U = .KeyCode.U, I = .KeyCode.I,
O = .KeyCode.O, P = .KeyCode.P,
}
Keybinds.(action, keyName)
KEY_MAP[keyName]
Keybinds.binds[action] = KEY_MAP[keyName]
Config.keybinds
Config.keybinds[action] = keyName
()
isMobile
UserInputService.InputBegan:((input, processed)
processed
input.KeyCode == Keybinds.binds.reclaim
AIState.mode == AIState.mode == AIState.mode ==
GhostController.()
VoidGUI.()
input.KeyCode == Keybinds.binds.toggleObs
AIState.isLearning
AIState.isLearning =
Observer.()
VoidGUI.()
AIState.isLearning =
Observer.()
VoidGUI.()
VoidGUI.()
input.KeyCode == Keybinds.binds.toggleLend
AIState.mode ==
GhostController.()
GhostController.()
VoidGUI.()
input.KeyCode == Keybinds.binds.toggleGUI
VoidGUI.()
)
(dt)
Config.scheduleEnabled
AIState.scheduleTimer += dt
startSec = (Config.scheduleStartMin ) *
durSec = (Config.scheduleDuration ) *
AIState.scheduleActive AIState.scheduleTimer >= startSec
AIState.scheduleActive =
GhostController.()
VoidGUI.()
AIState.scheduleActive AIState.scheduleTimer >= startSec + durSec
AIState.scheduleActive =
GhostController.()
AIState.scheduleTimer =
VoidGUI.()
lastLearnTick =
lastSaveTick =
lastThreatTick=
lastLLMTick =
RunService.Heartbeat:((dt)
(dt)
(dt)
AIState.tickCounter +=
lastThreatTick += dt
lastThreatTick >=
lastThreatTick =
isThreat, tName = ThreatDetector.()
isThreat AIState.threatDetected
ThreatDetector.(tName)
AIState.threatCooldown >
AIState.threatCooldown -=
AIState.threatCooldown <=
AIState.threatDetected =
lastLearnTick += dt
lastLearnTick >= LearnCooldown
lastLearnTick =
AIState.isLearning AIState.currentSession
char = LocalPlayer.Character
char
hrp = char:()
hrp
Observer.(hrp.Position)
AIState.mode == AIState.mode ==
lastLLMTick += dt
lastLLMTick >=
lastLLMTick =
AIState.isPaused
GhostController.()
AIState.mode ==
lastLLMTick += dt
lastLLMTick >= LLMController.callCooldown
lastLLMTick =
AIState.isPaused
LLMController.()
lastSaveTick += dt
lastSaveTick >=
lastSaveTick =
AIState.isLearning
()
)
VoidGUI = {}
VoidGUI.isOpen =
VoidGUI.tab =
screenGui, mainFrame, miniButton
COLORS = {
bg = .(, , ),
panel = .(, , ),
accent = .(, , ),
accentHot = .(, , ),
text = .(, , ),
textDim = .(, , ),
success = .(, , ),
warning = .(, , ),
danger = .(, , ),
border = .(, , ),
}
MODE_ICONS = {
idle = ,
observing = ,
ghost = ,
lend = ,
llm = ,
paused = ,
stealth = ,
}
(frame, handle)
dragging, dragStart, startPos
handle = handle frame
handle.InputBegan:((input)
input.UserInputType == .UserInputType.MouseButton1
input.UserInputType == .UserInputType.Touch
dragging =
dragStart = input.Position
startPos = frame.Position
)
UserInputService.InputChanged:((input)
dragging
input.UserInputType == .UserInputType.MouseMovement
input.UserInputType == .UserInputType.Touch
delta = input.Position - dragStart
frame.Position = .(
startPos.X.Scale,
startPos.X.Offset + delta.X,
startPos.Y.Scale,
startPos.Y.Offset + delta.Y
)
)
UserInputService.InputEnded:((input)
input.UserInputType == .UserInputType.MouseButton1
input.UserInputType == .UserInputType.Touch
dragging =
)
(parent, text, size, pos, color)
btn = .()
btn.Size = size
btn.Position = pos
btn.BackgroundColor3 = color COLORS.accent
btn.TextColor3 = COLORS.text
btn.Text = text
btn.Font = .Font.GothamBold
btn.TextSize = isMobile
btn.BorderSizePixel =
btn.AutoButtonColor =
btn.Parent = parent
corner = .()
corner.CornerRadius = .(, )
corner.Parent = btn
btn.MouseEnter:(()
TweenService:(btn, TweenInfo.(), {BackgroundColor3 = COLORS.accentHot}):()
)
btn.MouseLeave:(()
TweenService:(btn, TweenInfo.(), {BackgroundColor3 = color COLORS.accent}):()
)
btn
(parent, text, size, pos, textSize, color)
lbl = .()
lbl.Size = size
lbl.Position = pos
lbl.BackgroundTransparency =
lbl.TextColor3 = color COLORS.text
lbl.Text = text
lbl.Font = .Font.Gotham
lbl.TextSize = textSize
lbl.TextXAlignment = .TextXAlignment.Left
lbl.Parent = parent
lbl
(parent, pos, value, color)
track = .()
track.Size = .(, -, , )
track.Position = pos
track.BackgroundColor3= COLORS.border
track.BorderSizePixel =
track.Parent = parent
tc = .()
tc.CornerRadius = .(,)
tc.Parent = track
fill = .()
fill.Size = .(value/, , , )
fill.BackgroundColor3 = color COLORS.accent
fill.BorderSizePixel =
fill.Parent = track
fc = .()
fc.CornerRadius = .(,)
fc.Parent = fill
track, fill
VoidGUI.(msg)
AIState.isStealth
toast = .()
toast.Size = .(, , , )
toast.Position = .(, -, , )
toast.BackgroundColor3 = COLORS.panel
toast.TextColor3 = COLORS.text
toast.Text = msg
toast.Font = .Font.Gotham
toast.TextSize =
toast.BorderSizePixel =
toast.ZIndex =
toast.Parent = screenGui
corner = .()
corner.CornerRadius = .(,)
corner.Parent = toast
TweenService:(toast, TweenInfo.(), {BackgroundTransparency=}):()
.(, ()
TweenService:(toast, TweenInfo.(), {BackgroundTransparency=, TextTransparency=}):()
.()
toast:()
)
VoidGUI.()
AIState.isStealth MODE_ICONS.stealth
AIState.isPaused MODE_ICONS.paused
AIState.mode == MODE_ICONS.llm
MODE_ICONS[AIState.mode] MODE_ICONS.idle
VoidGUI.()
AIState.isStealth
AIState.isPaused
labels = {
idle = ,
observing = ,
ghost = ,
lend = ,
llm = ,
}
labels[AIState.mode]
lblMode, lblGame, lblSessions, lblActions, lblFPS
barMove, barUI, barSeq
btnLearn, btnGhost, btnLend, btnLLM, btnPause
VoidGUI.()
AIState.isStealth
place = ()
lblMode lblMode.Text = VoidGUI.() .. .. VoidGUI.()
lblGame lblGame.Text = .. (place place.placeName )
lblSessions
lblSessions.Text = .. (place #place.sessions )
lblActions
lblActions.Text = .. (place place.totalActions )
lblFPS
lblFPS.Text = .. FPS .. .. .(, LearnCooldown * )
place
c = place.confidence
barMove barMove.Size = .(c.movement/, , , )
barUI barUI.Size = .(c.ui/, , , )
barSeq barSeq.Size = .(c.sequences/,, , )
miniButton
miniButton.Text = VoidGUI.()
VoidGUI.()
mainFrame
mainFrame.Visible = mainFrame.Visible
miniButton
miniButton.Visible = mainFrame.Visible
VoidGUI.()
mainFrame
mainFrame.Visible =
miniButton miniButton.Visible =
VoidGUI.()
mainFrame
mainFrame.Visible =
miniButton miniButton.Visible =
VoidGUI.(container)
y =
lblMode = (container, ,
.(,-,,), .(,,,y), , COLORS.accent)
lblMode.Font = .Font.GothamBold
y +=
lblGame = (container, ,
.(,-,,), .(,,,y), , COLORS.textDim)
y +=
div = .()
div.Size = .(,-,,)
div.Position = .(,,,y)
div.BackgroundColor3 = COLORS.border
div.BorderSizePixel =
div.Parent = container
y +=
lblSessions = (container, ,
.(,-,,), .(,,,y), )
lblActions = (container, ,
.(,-,,), .(,,,y), )
y +=
lblFPS = (container, ,
.(,-,,), .(,,,y), , COLORS.textDim)
y +=
(container, , .(,-,,), .(,,,y), , COLORS.textDim)
y +=
(container, , .(,,,), .(,,,y), , COLORS.textDim)
_, mf = (container, .(,,,y+), , COLORS.success)
barMove = mf
y +=
(container, , .(,,,), .(,,,y), , COLORS.textDim)
_, uf = (container, .(,,,y+), , COLORS.accent)
barUI = uf
y +=
(container, , .(,,,), .(,,,y), , COLORS.textDim)
_, sf = (container, .(,,,y+), , COLORS.warning)
barSeq = sf
y +=
btnLearn = (container, ,
.(,-,,), .(,,,y), COLORS.success)
btnLearn.TextColor3 = .(,,)
y +=
btnGhost = (container, ,
.(,-,,), .(,,,y))
y +=
btnLend = (container, ,
.(,-,,), .(,,,y), COLORS.warning)
btnLend.TextColor3 = .(,,)
y +=
btnLLM = (container, ,
.(,-,,), .(,,,y), COLORS.panel)
btnLLM.TextColor3 = COLORS.textDim
y +=
btnPause = (container, ,
.(,-,,), .(,,,y), COLORS.panel)
btnReset = (container, ,
.(,-,,), .(,,,y), COLORS.danger)
y +=
btnLearn.MouseButton1Click:(()
AIState.isLearning
AIState.isLearning =
Observer.()
btnLearn.Text =
VoidGUI.()
AIState.isLearning =
Observer.()
btnLearn.Text =
VoidGUI.()
VoidGUI.()
)
btnGhost.MouseButton1Click:(()
AIState.mode ==
GhostController.()
btnGhost.Text =
GhostController.()
btnGhost.Text =
VoidGUI.()
)
btnLend.MouseButton1Click:(()
AIState.mode ==
GhostController.()
btnLend.Text =
GhostController.()
btnLend.Text =
isMobile
VoidGUI.()
VoidGUI.( .. (Config.keybinds Config.keybinds.reclaim ) .. )
VoidGUI.()
)
btnLLM.MouseButton1Click:(()
canUse, reason = LLMController.()
canUse
VoidGUI.( .. (reason ))
AIState.mode ==
GhostController.()
btnLLM.Text =
btnLLM.BackgroundColor3 = COLORS.panel
btnLLM.TextColor3 = COLORS.textDim
AIState.mode =
btnLLM.Text =
btnLLM.BackgroundColor3 = COLORS.accent
btnLLM.TextColor3 = COLORS.text
place = ()
place place.confidence.movement <
VoidGUI.()
VoidGUI.()
)
btnPause.MouseButton1Click:(()
AIState.isPaused = AIState.isPaused
btnPause.Text = AIState.isPaused
VoidGUI.(AIState.isPaused )
VoidGUI.()
)
btnReset.MouseButton1Click:(()
btnReset.Text =
.(, ()
btnReset.Text ==
btnReset.Text =
)
btnReset.Text ==
place = ()
place
place.sessions = {}
place.totalActions =
place.confidence = { movement=, ui=, sequences= }
()
VoidGUI.()
btnReset.Text =
VoidGUI.()
)
task.spawn(function()
while true do
task.wait(2)
local canUse = LLMController.canUseLLM()
if canUse and AIState.mode ~= "llm" then
btnLLM.BackgroundColor3 = COLORS.accent
btnLLM.TextColor3 = COLORS.text
elseif AIState.mode ~= "llm" then
btnLLM.BackgroundColor3 = COLORS.panel
btnLLM.TextColor3 = COLORS.textDim
end
end
end)
end
function VoidGUI.buildStatsTab(container)
local place = getCurrentPlace()
local y = 0
makeLabel(container, "📊 Stats Dashboard",
UDim2.new(1,-12,0,18), UDim2.new(0,6,0,y), 14, COLORS.accent).Font = Enum.Font.GothamBold
y += 24
local sessions = place and #place.sessions or 0
local actions = place and place.totalActions or 0
local playtime = place and place.totalPlaytime or 0
local brainSize = 0
if Exec.isFile(BrainPath) then
local raw = Exec.readFile(BrainPath)
brainSize = raw and #raw or 0
end
local stats = {
{"Sessions", sessions},
{"Total Actions", actions},
{"Playtime", string.format("%.0fm %.0fs", playtime/60, playtime%60)},
{"Brain File Size", string.format("%.1f KB", brainSize/1024)},
{"Current FPS", FPS},
{"Learn Cooldown", string.format("%.0fms", LearnCooldown*1000)},
{"Universe ID", getUniverseId()},
{"Place ID", getPlaceId()},
}
for _, stat in ipairs(stats) do
makeLabel(container, stat[1], UDim2.new(0.5,-6,0,16), UDim2.new(0,6,0,y), 11, COLORS.textDim)
makeLabel(container, tostring(stat[2]), UDim2.new(0.5,-6,0,16), UDim2.new(0.5,0,0,y), 11, COLORS.text)
y += 18
end
y += 6
makeLabel(container, "Top UI Buttons", UDim2.new(1,-12,0,16), UDim2.new(0,6,0,y), 11, COLORS.accent)
y += 18
if place then
local sorted = {}
for name, count in pairs(place.topUIButtons) do
table.insert(sorted, {name=name, count=count})
end
table.sort(sorted, function(a,b) return a.count > b.count end)
for i = 1, math.min(5, #sorted) do
local s = sorted[i]
makeLabel(container, s.name .. " x" .. s.count,
UDim2.new(1,-12,0,14), UDim2.new(0,6,0,y), 10, COLORS.text)
y += 15
end
end
end
function VoidGUI.buildReplayTab(container)
local y = 0
makeLabel(container, "📼 Session Replay",
UDim2.new(1,-12,0,18), UDim2.new(0,6,0,y), 14, COLORS.accent).Font = Enum.Font.GothamBold
y += 24
local place = getCurrentPlace()
if not place or #place.sessions == 0 then
makeLabel(container, "No sessions recorded yet.",
UDim2.new(1,-12,0,20), UDim2.new(0,6,0,y), 11, COLORS.textDim)
return
end
-- Scrollable session list
local scroll = Instance.new("ScrollingFrame")
scroll.Size = UDim2.new(1,0,0,180)
scroll.Position = UDim2.new(0,0,0,y)
scroll.BackgroundTransparency = 1
scroll.ScrollBarThickness = 3
scroll.ScrollBarImageColor3 = COLORS.accent
scroll.Parent = container
local layout = Instance.new("UIListLayout")
layout.Padding = UDim.new(0,4)
layout.Parent = scroll
for i, session in ipairs(place.sessions) do
local row = Instance.new("Frame")
row.Size = UDim2.new(1,-6,0,40)
row.BackgroundColor3 = COLORS.panel
row.BorderSizePixel = 0
row.Parent = scroll
local rc = Instance.new("UICorner")
rc.CornerRadius = UDim.new(0,6)
rc.Parent = row
local starIcon = session.starred and "⭐" or "☆"
local lbl = makeLabel(row,
string.format("%s Session %d — %d actions", starIcon, i, #(session.actions or {})),
UDim2.new(0.7,0,0,18), UDim2.new(0,6,0,4), 10)
makeLabel(row,
"Priority: " .. (session.priority or 1),
UDim2.new(0.7,0,0,14), UDim2.new(0,6,0,22), 9, COLORS.textDim)
-- Star button
local starBtn = makeButton(row, starIcon, UDim2.new(0,28,0,28), UDim2.new(1,-64,0,6), COLORS.panel)
starBtn.MouseButton1Click:Connect(function()
session.starred = not session.starred
starBtn.Text = session.starred and "⭐" or "☆"
lbl.Text = string.format("%s Session %d — %d actions",
session.starred and "⭐" or "☆", i, #(session.actions or {}))
saveBrain()
end)
-- Delete button
local delBtn = makeButton(row, "🗑", UDim2.new(0,28,0,28), UDim2.new(1,-32,0,6), COLORS.danger)
delBtn.MouseButton1Click:Connect(function()
table.remove(place.sessions, i)
saveBrain()
row:Destroy()
VoidGUI.notify("Session " .. i .. " deleted")
Observer.updateConfidence(place)
end)
-- Priority +/-
local prioUp = makeButton(row, "+", UDim2.new(0,18,0,18), UDim2.new(1,-96,0,6), COLORS.success)
local prioDn = makeButton(row, "-", UDim2.new(0,18,0,18), UDim2.new(1,-96,0,24), COLORS.danger)
prioUp.TextColor3 = Color3.fromRGB(20,20,30)
prioUp.MouseButton1Click:Connect(function()
session.priority = math.min(5, (session.priority or 1) + 1)
saveBrain()
end)
prioDn.MouseButton1Click:Connect(function()
session.priority = math.max(1, (session.priority or 1) - 1)
saveBrain()
end)
end
scroll.CanvasSize = UDim2.new(0,0,0,#place.sessions * 44)
end
function VoidGUI.buildSettingsTab(container)
local y = 0
makeLabel(container, "⚙ Settings",
UDim2.new(1,-12,0,18), UDim2.new(0,6,0,y), 14, COLORS.accent).Font = Enum.Font.GothamBold
y += 24
-- Persona
makeLabel(container, "Persona", UDim2.new(1,-12,0,14), UDim2.new(0,6,0,y), 11, COLORS.textDim)
y += 16
local personas = {"Mirror","Aggressive","Passive"}
for _, pname in ipairs(personas) do
local isActive = Config.activePersona == pname
local pbtn = makeButton(container, pname,
UDim2.new(1/3,-5,0,24),
UDim2.new((table.find(personas,pname)-1)/3, 3, 0, y),
isActive and COLORS.accent or COLORS.panel)
pbtn.MouseButton1Click:Connect(function()
Config.activePersona = pname
AIState.persona = pname
saveConfig()
VoidGUI.notify("Persona: " .. pname)
end)
end
y += 30
-- LLM Provider
makeLabel(container, "LLM Provider", UDim2.new(1,-12,0,14), UDim2.new(0,6,0,y), 11, COLORS.textDim)
y += 16
local providers = {"claude","chatgpt","gemini"}
for _, prov in ipairs(providers) do
local isActive = Config.activeProvider == prov
local pvbtn = makeButton(container, prov,
UDim2.new(1/3,-5,0,24),
UDim2.new((table.find(providers,prov)-1)/3, 3, 0, y),
isActive and COLORS.accent or COLORS.panel)
pvbtn.MouseButton1Click:Connect(function()
Config.activeProvider = prov
saveConfig()
VoidGUI.notify("Provider: " .. prov)
end)
end
y += 30
-- API Key input
makeLabel(container, "API Key (active provider)",
UDim2.new(1,-12,0,14), UDim2.new(0,6,0,y), 11, COLORS.textDim)
y += 16
local keyBox = Instance.new("TextBox")
keyBox.Size = UDim2.new(1,-12,0,28)
keyBox.Position = UDim2.new(0,6,0,y)
keyBox.BackgroundColor3 = COLORS.panel
keyBox.TextColor3 = COLORS.text
keyBox.PlaceholderText = "Enter API key..."
keyBox.PlaceholderColor3= COLORS.textDim
keyBox.Text = Config.apiKeys[Config.activeProvider] or ""
keyBox.Font = Enum.Font.Code
keyBox.TextSize = 10
keyBox.ClearTextOnFocus = false
keyBox.BorderSizePixel = 0
keyBox.TextXAlignment = Enum.TextXAlignment.Left
keyBox.TextEditable = true
keyBox.Parent = container
local kbc = Instance.new("UICorner"); kbc.CornerRadius = UDim.new(0,6); kbc.Parent = keyBox
local kbp = Instance.new("UIPadding"); kbp.PaddingLeft = UDim.new(0,6); kbp.Parent = keyBox
keyBox.FocusLost:Connect(function()
Config.apiKeys[Config.activeProvider] = keyBox.Text
saveConfig()
VoidGUI.notify("API key saved")
end)
y += 34
-- Keybinds (PC only)
if isPC then
makeLabel(container, "Keybinds (PC)",
UDim2.new(1,-12,0,14), UDim2.new(0,6,0,y), 11, COLORS.textDim)
y += 16
local bindNames = {
{key="reclaim", label="Reclaim"},
{key="toggleObs", label="Observe"},
{key="toggleLend",label="Lend"},
{key="toggleGUI", label="GUI"},
}
for _, b in ipairs(bindNames) do
makeLabel(container, b.label,
UDim2.new(0.4,0,0,22), UDim2.new(0,6,0,y), 10)
local bindBox = Instance.new("TextBox")
bindBox.Size = UDim2.new(0.3,-3,0,22)
bindBox.Position = UDim2.new(0.4,3,0,y)
bindBox.BackgroundColor3 = COLORS.panel
bindBox.TextColor3 = COLORS.text
bindBox.Text = Config.keybinds and Config.keybinds[b.key] or "F"
bindBox.Font = Enum.Font.GothamBold
bindBox.TextSize = 11
bindBox.BorderSizePixel = 0
bindBox.Parent = container
local bbc = Instance.new("UICorner"); bbc.CornerRadius = UDim.new(0,4); bbc.Parent = bindBox
bindBox.FocusLost:Connect(function()
local k = bindBox.Text:upper()
Keybinds.setKey(b.key, k)
bindBox.Text = k
end)
y += 26
end
end
-- Stealth toggle
local stealthBtn = makeButton(container,
Config.stealthMode and "🔇 Stealth: ON" or "🔈 Stealth: OFF",
UDim2.new(1,-12,0,28), UDim2.new(0,6,0,y),
Config.stealthMode and COLORS.accent or COLORS.panel)
stealthBtn.MouseButton1Click:Connect(function()
Config.stealthMode = not Config.stealthMode
AIState.isStealth = Config.stealthMode
stealthBtn.Text = Config.stealthMode and "🔇 Stealth: ON" or "🔈 Stealth: OFF"
stealthBtn.BackgroundColor3 = Config.stealthMode and COLORS.accent or COLORS.panel
saveConfig()
if Config.stealthMode then VoidGUI.minimize() end
end)
y += 34
-- Schedule
makeLabel(container, "Schedule (minutes)",
UDim2.new(1,-12,0,14), UDim2.new(0,6,0,y), 11, COLORS.textDim)
y += 16
local schedBtn = makeButton(container,
Config.scheduleEnabled and "⏱ Schedule: ON" or "⏱ Schedule: OFF",
UDim2.new(1,-12,0,28), UDim2.new(0,6,0,y),
Config.scheduleEnabled and COLORS.success or COLORS.panel)
schedBtn.MouseButton1Click:Connect(function()
Config.scheduleEnabled = not Config.scheduleEnabled
schedBtn.Text = Config.scheduleEnabled and "⏱ Schedule: ON" or "⏱ Schedule: OFF"
schedBtn.BackgroundColor3 = Config.scheduleEnabled and COLORS.success or COLORS.panel
saveConfig()
end)
end
function VoidGUI.build()
-- Main ScreenGui
screenGui = Instance.new("ScreenGui")
screenGui.Name = "VoidAIGui"
screenGui.ResetOnSpawn = false
screenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
screenGui.IgnoreGuiInset = true
screenGui.Parent = PlayerGui
-- ---- MINI BUTTON (shown when minimized) ----
miniButton = Instance.new("TextButton")
miniButton.Size = UDim2.new(0, isMobile and 52 or 44, 0, isMobile and 52 or 44)
miniButton.Position = UDim2.new(0, 10, 0.4, 0)
miniButton.BackgroundColor3 = COLORS.accent
miniButton.TextColor3 = COLORS.text
miniButton.Text = "🌙"
miniButton.Font = Enum.Font.GothamBold
miniButton.TextSize = isMobile and 22 or 20
miniButton.BorderSizePixel = 0
miniButton.Visible = false
miniButton.ZIndex = 50
miniButton.Parent = screenGui
local mc = Instance.new("UICorner")
mc.CornerRadius = UDim.new(1,0)
mc.Parent = miniButton
makeDraggable(miniButton)
miniButton.MouseButton1Click:Connect(function()
VoidGUI.maximize()
end)
-- ---- MAIN PANEL ----
local panelW = isMobile and 420 or 480
local panelH = isMobile and 280 or 320
mainFrame = Instance.new("Frame")
mainFrame.Name = "VoidAIPanel"
mainFrame.Size = UDim2.new(0, panelW, 0, panelH)
mainFrame.Position = UDim2.new(0, 10, 0.1, 0)
mainFrame.BackgroundColor3 = COLORS.bg
mainFrame.BorderSizePixel = 0
mainFrame.ClipsDescendants = true
mainFrame.Parent = screenGui
local mc2 = Instance.new("UICorner")
mc2.CornerRadius = UDim.new(0,10)
mc2.Parent = mainFrame
-- Border glow
local stroke = Instance.new("UIStroke")
stroke.Color = COLORS.accent
stroke.Thickness = 1
stroke.Transparency = 0.5
stroke.Parent = mainFrame
-- Title bar
local titleBar = Instance.new("Frame")
titleBar.Size = UDim2.new(1,0,0,32)
titleBar.BackgroundColor3 = COLORS.panel
titleBar.BorderSizePixel = 0
titleBar.Parent = mainFrame
local tbc = Instance.new("UICorner"); tbc.CornerRadius = UDim.new(0,10); tbc.Parent = titleBar
makeLabel(titleBar, " 🤖 VoidAI",
UDim2.new(0.7,0,1,0), UDim2.new(0,0,0,0), 13, COLORS.text).Font = Enum.Font.GothamBold
local closeBtn = Instance.new("TextButton")
closeBtn.Size = UDim2.new(0,28,0,28)
closeBtn.Position = UDim2.new(1,-32,0,2)
closeBtn.BackgroundColor3 = COLORS.danger
closeBtn.Text = "×"
closeBtn.Font = Enum.Font.GothamBold
closeBtn.TextSize = 16
closeBtn.TextColor3 = COLORS.text
closeBtn.BorderSizePixel = 0
closeBtn.Parent = titleBar
local cc = Instance.new("UICorner"); cc.CornerRadius = UDim.new(0,6); cc.Parent = closeBtn
closeBtn.MouseButton1Click:Connect(function()
VoidGUI.minimize()
end)
makeDraggable(mainFrame, titleBar)
-- ---- RESIZE HANDLE (bottom-right corner) ----
local resizeHandle = Instance.new("TextButton")
resizeHandle.Size = UDim2.new(0, 16, 0, 16)
resizeHandle.Position = UDim2.new(1, -16, 1, -16)
resizeHandle.BackgroundColor3 = COLORS.accent
resizeHandle.Text = ""
resizeHandle.BorderSizePixel = 0
resizeHandle.ZIndex = 20
resizeHandle.Parent = mainFrame
local rhc = Instance.new("UICorner"); rhc.CornerRadius = UDim.new(0,3); rhc.Parent = resizeHandle
-- small diagonal lines icon
local rhLbl = Instance.new("TextLabel")
rhLbl.Size = UDim2.new(1,0,1,0); rhLbl.BackgroundTransparency=1
rhLbl.Text = "⤡"; rhLbl.TextSize=10; rhLbl.TextColor3=COLORS.text
rhLbl.Font=Enum.Font.GothamBold; rhLbl.Parent=resizeHandle
do
local resizing, resizeStart, startSize = false, nil, nil
resizeHandle.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or
input.UserInputType == Enum.UserInputType.Touch then
resizing = true
resizeStart = input.Position
startSize = mainFrame.AbsoluteSize
end
end)
UserInputService.InputChanged:Connect(function(input)
if not resizing then return end
if input.UserInputType == Enum.UserInputType.MouseMovement or
input.UserInputType == Enum.UserInputType.Touch then
local delta = input.Position - resizeStart
local newW = math.clamp(startSize.X + delta.X, 320, 700)
local newH = math.clamp(startSize.Y + delta.Y, 220, 600)
mainFrame.Size = UDim2.new(0, newW, 0, newH)
end
end)
UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or
input.UserInputType == Enum.UserInputType.Touch then
resizing = false
end
end)
end
-- Tab bar
local tabBar = Instance.new("Frame")
tabBar.Size = UDim2.new(1,0,0,28)
tabBar.Position = UDim2.new(0,0,0,32)
tabBar.BackgroundColor3 = COLORS.panel
tabBar.BorderSizePixel = 0
tabBar.Parent = mainFrame
local tabs = {
{id="main", label="Main"},
{id="stats", label="Stats"},
{id="replay", label="Replay"},
{id="settings", label="Setup"},
}
local tabContents = {}
for i, tab in ipairs(tabs) do
local tbtn = Instance.new("TextButton")
tbtn.Size = UDim2.new(1/#tabs,0,1,0)
tbtn.Position = UDim2.new((i-1)/#tabs,0,0,0)
tbtn.BackgroundTransparency = 1
tbtn.Text = tab.label
tbtn.Font = Enum.Font.GothamBold
tbtn.TextSize = 10
tbtn.TextColor3 = COLORS.textDim
tbtn.Parent = tabBar
-- Content frame
local content = Instance.new("ScrollingFrame")
content.Size = UDim2.new(1,0,1,0)
content.Position = UDim2.new(0,0,0,60)
content.BackgroundTransparency = 1
content.ScrollBarThickness = 3
content.ScrollBarImageColor3 = COLORS.accent
content.CanvasSize = UDim2.new(0,0,0,500)
content.Visible = (tab.id == "main")
content.Parent = mainFrame
tabContents[tab.id] = content
tbtn.MouseButton1Click:Connect(function()
VoidGUI.tab = tab.id
for _, tc in pairs(tabContents) do tc.Visible = false end
content.Visible = true
for _, child in ipairs(tabBar:GetChildren()) do
if child:IsA("TextButton") then
child.TextColor3 = COLORS.textDim
end
end
tbtn.TextColor3 = COLORS.accent
end)
-- Active underline
if tab.id == "main" then
tbtn.TextColor3 = COLORS.accent
end
end
-- Build tab contents
VoidGUI.buildMainTab(tabContents["main"])
VoidGUI.buildStatsTab(tabContents["stats"])
VoidGUI.buildReplayTab(tabContents["replay"])
VoidGUI.buildSettingsTab(tabContents["settings"])
-- Mobile floating action buttons (reclaim etc)
if isMobile then
local reclaimBtn = Instance.new("TextButton")
reclaimBtn.Size = UDim2.new(0,60,0,60)
reclaimBtn.Position = UDim2.new(1,-70,0.8,0)
reclaimBtn.BackgroundColor3 = COLORS.danger
reclaimBtn.Text = "✋"
reclaimBtn.Font = Enum.Font.GothamBold
reclaimBtn.TextSize = 26
reclaimBtn.TextColor3 = COLORS.text
reclaimBtn.BorderSizePixel = 0
reclaimBtn.Visible = false
reclaimBtn.ZIndex = 60
reclaimBtn.Parent = screenGui
local rc3 = Instance.new("UICorner"); rc3.CornerRadius = UDim.new(1,0); rc3.Parent = reclaimBtn
makeDraggable(reclaimBtn)
reclaimBtn.MouseButton1Click:Connect(function()
GhostController.reclaim()
VoidGUI.updateDisplay()
end)
-- Show reclaim button only when AI is in control
RunService.Heartbeat:Connect(function()
reclaimBtn.Visible = (AIState.mode == "lend" or AIState.mode == "ghost" or AIState.mode == "llm")
end)
end
end
-- ============================================================
-- SECTION 20: THREAT HUD
-- ============================================================
local ThreatHUD = {}
local function buildThreatHUD()
if AIState.isStealth or not screenGui then return end
local indicator = Instance.new("Frame")
indicator.Name = "ThreatIndicator"
indicator.Size = UDim2.new(0, 190, 0, 30)
indicator.Position = UDim2.new(0.5, -95, 0, 8)
indicator.BackgroundColor3 = Color3.fromRGB(180, 30, 30)
indicator.BorderSizePixel = 0
indicator.Visible = false
indicator.ZIndex = 80
indicator.Parent = screenGui
local ic = Instance.new("UICorner")
ic.CornerRadius = UDim.new(0, 8)
ic.Parent = indicator
local lbl = Instance.new("TextLabel")
lbl.Size = UDim2.new(1, 0, 1, 0)
lbl.BackgroundTransparency = 1
lbl.TextColor3 = Color3.fromRGB(255, 255, 255)
lbl.Font = Enum.Font.GothamBold
lbl.TextSize = 13
lbl.Text = "🚨 THREAT DETECTED"
lbl.Parent = indicator
ThreatHUD.frame = indicator
ThreatHUD.label = lbl
-- Pulse
task.spawn(function()
while indicator and indicator.Parent do
TweenService:Create(indicator, TweenInfo.new(0.3),
{BackgroundTransparency = 0.5}):Play()
task.wait(0.3)
TweenService:Create(indicator, TweenInfo.new(0.3),
{BackgroundTransparency = 0}):Play()
task.wait(0.3)
end
end)
RunService.Heartbeat:Connect(function()
if indicator and indicator.Parent then
indicator.Visible = AIState.threatDetected and not AIState.isStealth
end
end)
end
-- ============================================================
-- SECTION 21: SEQUENCE DETECTOR
-- ============================================================
local SequenceDetector = {}
SequenceDetector.windowSize = 5
SequenceDetector.knownPatterns = {}
function SequenceDetector.analyze()
local place = getCurrentPlace()
if not place or #place.sessions < 2 then return end
local seqMap = {}
for _, session in ipairs(place.sessions) do
local actions = session.actions or {}
for i = 1, #actions - SequenceDetector.windowSize do
local key = ""
for j = i, i + SequenceDetector.windowSize - 1 do
key = key .. (actions[j].type or "?") .. "|"
end
seqMap[key] = (seqMap[key] or 0) + 1
end
end
SequenceDetector.knownPatterns = {}
for seq, count in pairs(seqMap) do
if count >= 3 then
table.insert(SequenceDetector.knownPatterns, {seq = seq, count = count})
end
end
table.sort(SequenceDetector.knownPatterns, function(a, b) return a.count > b.count end)
end
function SequenceDetector.matchCurrent(recentActions)
if #SequenceDetector.knownPatterns == 0 then return nil end
local key = ""
local start = math.max(1, #recentActions - SequenceDetector.windowSize + 1)
for i = start, #recentActions do
key = key .. (recentActions[i].type or "?") .. "|"
end
for _, pat in ipairs(SequenceDetector.knownPatterns) do
if pat.seq == key then return pat end
end
return nil
end
task.spawn(function()
while true do
task.wait(60)
SequenceDetector.analyze()
end
end)
-- ============================================================
-- SECTION 22: SMART REPLAY (pattern-aware)
-- ============================================================
local _originalStepReplay = GhostController.stepReplay
function GhostController.stepReplay()
if not GhostController.replaySession then return end
local actions = GhostController.replaySession.actions
if not actions or #actions == 0 then return end
-- Pattern-based decision
local place = getCurrentPlace()
if place and #SequenceDetector.knownPatterns > 0 then
local match = SequenceDetector.matchCurrent(actions)
if match then
for _, session in ipairs(place.sessions) do
local sactions = session.actions or {}
for i = 1, #sactions - SequenceDetector.windowSize do
local key = ""
for j = i, i + SequenceDetector.windowSize - 1 do
key = key .. (sactions[j].type or "?") .. "|"
end
if key == match.seq and sactions[i + SequenceDetector.windowSize] then
local nextAction = sactions[i + SequenceDetector.windowSize]
if nextAction.type == "move" then
GhostController.executeMove(nextAction.data)
elseif nextAction.type == "ui_click" then
GhostController.executeUIClick(nextAction.data)
elseif nextAction.type == "jump" then
local char = LocalPlayer.Character
local hum = char and char:FindFirstChildOfClass("Humanoid")
if hum then hum.Jump = true end
end
return
end
end
end
end
end
-- Linear fallback
if GhostController.replayIndex > #actions then
GhostController.replayIndex = 1
return
end
local action = actions[GhostController.replayIndex]
GhostController.replayIndex += 1
local char = LocalPlayer.Character
local hum = char and char:FindFirstChildOfClass("Humanoid")
if action.type == "move" then
GhostController.executeMove(action.data)
elseif action.type == "ui_click" then
GhostController.executeUIClick(action.data)
elseif action.type == "jump" then
if hum then hum.Jump = true end
elseif action.type == "sprint_start" then
if hum then hum.WalkSpeed = AIState.persona == "Passive" and 14 or 24 end
elseif action.type == "sprint_end" then
if hum then hum.WalkSpeed = AIState.persona == "Aggressive" and 20 or 16 end
end
end
-- ============================================================
-- SECTION 23: JUMP + SPRINT TRACKING (Event-driven)
-- ============================================================
if not isMobile then
UserInputService.InputBegan:Connect(function(input, processed)
if processed then return end
if not AIState.isLearning or not AIState.currentSession then return end
if input.KeyCode == Enum.KeyCode.Space then
table.insert(AIState.currentSession.actions, {
type = "jump",
data = {t = os.time() - AIState.sessionStart}
})
elseif input.KeyCode == Enum.KeyCode.LeftShift then
table.insert(AIState.currentSession.actions, {
type = "sprint_start",
data = {t = os.time() - AIState.sessionStart}
})
end
end)
UserInputService.InputEnded:Connect(function(input, processed)
if not AIState.isLearning or not AIState.currentSession then return end
if input.KeyCode == Enum.KeyCode.LeftShift then
table.insert(AIState.currentSession.actions, {
type = "sprint_end",
data = {t = os.time() - AIState.sessionStart}
})
end
end)
end
-- Re-hook on respawn
LocalPlayer.CharacterAdded:Connect(function(char)
task.wait(1)
local hum = char:WaitForChild("Humanoid", 5)
if hum then
if isMobile then
hum.Jumping:Connect(function(active)
if active and AIState.isLearning and AIState.currentSession then
table.insert(AIState.currentSession.actions, {
type = "jump",
data = {t = os.time() - AIState.sessionStart}
})
end
end)
end
-- Reset position tracking
local hrp = char:WaitForChild("HumanoidRootPart", 5)
if hrp then Observer.lastPosition = hrp.Position end
end
-- Auto-pause AI control during respawn
if AIState.mode == "lend" or AIState.mode == "ghost" then
AIState.isPaused = true
task.wait(2)
AIState.isPaused = false
end
end)
-- ============================================================
-- SECTION 24: OTHER PLAYER OBSERVER (low priority)
-- ============================================================
RunService.Heartbeat:Connect(function()
if AIState.tickCounter % 5 ~= 0 then return end
if not AIState.isLearning then return end
for _, p in ipairs(Players:GetPlayers()) do
if p ~= LocalPlayer and p.Character then
local hum = p.Character:FindFirstChildOfClass("Humanoid")
local hrp = p.Character:FindFirstChild("HumanoidRootPart")
if hum and hrp and hum.WalkSpeed > 20 then
-- Multiple players fleeing = potential threat signal
-- Already handled in ThreatDetector.scan()
end
end
end
end)
-- ============================================================
-- SECTION 25: CONFIDENCE COLOR CODING
-- ============================================================
local function getConfidenceColor(value)
if value >= 70 then return COLORS.success
elseif value >= 40 then return COLORS.warning
else return COLORS.danger end
end
local _origUpdateDisplay = VoidGUI.updateDisplay
function VoidGUI.updateDisplay()
_origUpdateDisplay()
local place = getCurrentPlace()
if place then
if barMove then barMove.BackgroundColor3 = getConfidenceColor(place.confidence.movement) end
if barUI then barUI.BackgroundColor3 = getConfidenceColor(place.confidence.ui) end
if barSeq then barSeq.BackgroundColor3 = getConfidenceColor(place.confidence.sequences) end
end
end
-- ============================================================
-- SECTION 26: LOW CONFIDENCE WARNING
-- ============================================================
local _origStartGhost = GhostController.startGhost
function GhostController.startGhost()
local place = getCurrentPlace()
if place then
local avg = (place.confidence.movement + place.confidence.ui + place.confidence.sequences) / 3
if avg < 20 then
VoidGUI.notify("⚠ Very low confidence (" .. math.floor(avg) .. "%). Train more!")
task.wait(1.5)
elseif avg < 40 then
VoidGUI.notify("⚠ Low confidence (" .. math.floor(avg) .. "%). AI may be rough.")
end
end
return _origStartGhost()
end
-- ============================================================
-- SECTION 27: CHAT COMMANDS
-- ============================================================
local CHAT_PREFIX = "/void"
local CHAT_COMMANDS = {
learn = function() AIState.isLearning = true; Observer.startSession(); VoidGUI.notify("Learning started") end,
stop = function() AIState.isLearning = false; Observer.endSession(); VoidGUI.notify("Learning stopped") end,
ghost = function() GhostController.startGhost() end,
lend = function() GhostController.startLend() end,
reclaim = function() GhostController.reclaim() end,
pause = function()
AIState.isPaused = not AIState.isPaused
VoidGUI.notify(AIState.isPaused and "Paused" or "Resumed")
end,
stealth = function()
Config.stealthMode = not Config.stealthMode
AIState.isStealth = Config.stealthMode
saveConfig()
if Config.stealthMode then VoidGUI.minimize() else VoidGUI.maximize() end
end,
safe = function() Observer.tagCurrentZone("safe"); VoidGUI.notify("Zone: SAFE") end,
danger = function() Observer.tagCurrentZone("danger"); VoidGUI.notify("Zone: DANGER") end,
exit = function() Observer.tagCurrentZone("exit"); VoidGUI.notify("Zone: EXIT") end,
item = function() Observer.tagCurrentZone("item_spawn"); VoidGUI.notify("Zone: ITEM") end,
save = function() saveBrain(); VoidGUI.notify("Brain saved!") end,
llm = function()
local canUse, reason = LLMController.canUseLLM()
if canUse then AIState.mode = "llm"; VoidGUI.notify("LLM mode ON")
else VoidGUI.notify("LLM blocked: " .. (reason or "?")) end
end,
status = function()
local place = getCurrentPlace()
VoidGUI.notify(string.format("Mode:%s Sess:%d Move:%d%% UI:%d%%",
AIState.mode,
place and #place.sessions or 0,
place and place.confidence.movement or 0,
place and place.confidence.ui or 0))
end,
help = function()
VoidGUI.notify("Cmds: learn stop ghost lend reclaim pause stealth safe danger exit item save llm status")
end,
}
pcall(function()
LocalPlayer.Chatted:Connect(function(msg)
local parts = msg:lower():split(" ")
if parts[1] ~= CHAT_PREFIX then return end
local cmd = parts[2]
if cmd and CHAT_COMMANDS[cmd] then
CHAT_COMMANDS[cmd]()
VoidGUI.updateDisplay()
end
end)
end)
-- ============================================================
-- SECTION 28: MENTAL MAP VISUALIZER
-- ============================================================
local function buildMentalMapView(parent, yPos)
local place = getCurrentPlace()
if not place then return end
local TAG_COLORS = {
visited = Color3.fromRGB(50, 50, 90),
safe = Color3.fromRGB(87, 242, 135),
danger = Color3.fromRGB(237, 66, 69),
item_spawn = Color3.fromRGB(254, 231, 92),
exit = Color3.fromRGB(88, 101, 242),
}
local mapFrame = Instance.new("Frame")
mapFrame.Size = UDim2.new(1, -12, 0, 110)
mapFrame.Position = UDim2.new(0, 6, 0, yPos)
mapFrame.BackgroundColor3 = Color3.fromRGB(8, 8, 14)
mapFrame.BorderSizePixel = 0
mapFrame.ClipsDescendants = true
mapFrame.Parent = parent
local mc = Instance.new("UICorner"); mc.CornerRadius = UDim.new(0,6); mc.Parent = mapFrame
makeLabel(mapFrame, "🗺 Mental Map", UDim2.new(1,0,0,14), UDim2.new(0,4,0,2), 9, COLORS.textDim)
local landmarks = place.mentalMap.landmarks or {}
local minX, maxX, minZ, maxZ = math.huge, -math.huge, math.huge, -math.huge
for _, lm in pairs(landmarks) do
if lm.pos.x < minX then minX = lm.pos.x end
if lm.pos.x > maxX then maxX = lm.pos.x end
if lm.pos.z < minZ then minZ = lm.pos.z end
if lm.pos.z > maxZ then maxZ = lm.pos.z end
end
local rangeX = math.max(1, maxX - minX)
local rangeZ = math.max(1, maxZ - minZ)
local dotCount = 0
for _, lm in pairs(landmarks) do
if dotCount >= 150 then break end
dotCount += 1
local nx = (lm.pos.x - minX) / rangeX
local nz = (lm.pos.z - minZ) / rangeZ
local dot = Instance.new("Frame")
dot.Size = UDim2.new(0, 4, 0, 4)
dot.Position = UDim2.new(nx * 0.9 + 0.05, -2, nz * 0.7 + 0.15, -2)
dot.BackgroundColor3 = TAG_COLORS[lm.tag] or TAG_COLORS.visited
dot.BorderSizePixel = 0
dot.Parent = mapFrame
local dc = Instance.new("UICorner"); dc.CornerRadius = UDim.new(1,0); dc.Parent = dot
end
-- Player position dot
local char = LocalPlayer.Character
if char then
local hrp = char:FindFirstChild("HumanoidRootPart")
if hrp then
local nx = rangeX > 1 and (hrp.Position.X - minX) / rangeX or 0.5
local nz = rangeZ > 1 and (hrp.Position.Z - minZ) / rangeZ or 0.5
local pd = Instance.new("Frame")
pd.Size = UDim2.new(0, 7, 0, 7)
pd.Position = UDim2.new(math.clamp(nx*0.9+0.05,0,1), -3, math.clamp(nz*0.7+0.15,0,1), -3)
pd.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
pd.BorderSizePixel = 0
pd.ZIndex = 5
pd.Parent = mapFrame
local pdc = Instance.new("UICorner"); pdc.CornerRadius = UDim.new(1,0); pdc.Parent = pd
end
end
-- Legend row
local legendData = {{"safe","Safe"},{"danger","Danger"},{"exit","Exit"},{"item_spawn","Item"}}
local lx = 4
for _, li in ipairs(legendData) do
local ldot = Instance.new("Frame")
ldot.Size = UDim2.new(0,5,0,5); ldot.Position = UDim2.new(0,lx,1,-14)
ldot.BackgroundColor3 = TAG_COLORS[li[1]]; ldot.BorderSizePixel = 0; ldot.Parent = mapFrame
local lc = Instance.new("UICorner"); lc.CornerRadius = UDim.new(1,0); lc.Parent = ldot
makeLabel(mapFrame, li[2], UDim2.new(0,28,0,10), UDim2.new(0,lx+7,1,-16), 8, COLORS.textDim)
lx += 40
end
end
-- ============================================================
-- SECTION 29: BRAIN EXPORT
-- ============================================================
local function exportBrainSummary()
local place = getCurrentPlace()
if not place then VoidGUI.notify("No brain loaded"); return end
local lines = {
"=== VoidAI Brain Export ===",
"Universe: " .. getUniverseId(),
"Place: " .. getPlaceId() .. " (" .. (place.placeName or "?") .. ")",
"Sessions: " .. #place.sessions,
"Total Actions: " .. (place.totalActions or 0),
string.format("Confidence - Move:%d%% UI:%d%% Seq:%d%%",
place.confidence.movement, place.confidence.ui, place.confidence.sequences),
"",
"--- Top UI Buttons ---",
}
local sorted = {}
for name, count in pairs(place.topUIButtons or {}) do
table.insert(sorted, name .. ": " .. count .. "x")
end
table.sort(sorted)
for _, s in ipairs(sorted) do table.insert(lines, " " .. s) end
table.insert(lines, "")
table.insert(lines, "--- Mental Map Landmarks ---")
local lmCount = 0
for _, lm in pairs(place.mentalMap.landmarks or {}) do
lmCount += 1
table.insert(lines, string.format(" [%s] (%.0f,%.0f,%.0f) visits:%d",
lm.tag, lm.pos.x, lm.pos.y, lm.pos.z, lm.visits))
end
if lmCount == 0 then table.insert(lines, " (none yet)") end
table.insert(lines, "")
table.insert(lines, "--- Known Patterns ---")
if #SequenceDetector.knownPatterns > 0 then
for i, pat in ipairs(SequenceDetector.knownPatterns) do
if i > 10 then break end
table.insert(lines, string.format(" Pattern %d (x%d): %s", i, pat.count, pat.seq))
end
else
table.insert(lines, " (none — train 2+ sessions first)")
end
table.insert(lines, "")
table.insert(lines, "Exported: " .. os.date and os.date("%Y-%m-%d %H:%M:%S") or tostring(os.time()))
local path = BRAIN_FOLDER .. "/export_" .. getPlaceId() .. ".txt"
Exec.writeFile(path, table.concat(lines, "\n"))
VoidGUI.notify("Exported to " .. path)
end
-- ============================================================
-- SECTION 30: HORROR GAME AUTO-DETECT
-- ============================================================
local HORROR_KEYWORDS = {
"doors","horror","scary","haunted","dark","nightmare",
"paranormal","escape","survival","entity","monster","backrooms",
}
local function detectAndConfigureHorror()
local place = getCurrentPlace()
if not place then return end
local gname = (place.placeName or ""):lower()
local isHorror = false
for _, kw in ipairs(HORROR_KEYWORDS) do
if gname:find(kw) then isHorror = true; break end
end
if not isHorror then return end
VoidGUI.notify("🚨 Horror game detected — threat detection enhanced")
-- Pre-tag safe objects in workspace
for _, obj in ipairs(workspace:GetDescendants()) do
if obj:IsA("BasePart") then
for _, safe in ipairs(BASE_KNOWLEDGE.horrorDefaults.safeObjects) do
if obj.Name:lower():find(safe:lower()) then
local posKey = math.floor(obj.Position.X/10) .. "_" .. math.floor(obj.Position.Z/10)
if not place.mentalMap.landmarks[posKey] then
place.mentalMap.landmarks[posKey] = {
pos = {x=obj.Position.X, y=obj.Position.Y, z=obj.Position.Z},
tag = "safe",
visits= 1,
note = "auto:" .. obj.Name,
}
end
end
end
end
end
end
-- ============================================================
-- SECTION 31: SCHEDULE COUNTDOWN
-- ============================================================
task.spawn(function()
while true do
task.wait(10)
if Config.scheduleEnabled and not AIState.scheduleActive then
local startSec = (Config.scheduleStartMin or 0) * 60
local remaining = startSec - AIState.scheduleTimer
if remaining > 0 and remaining < 120 then
VoidGUI.notify(string.format("⏱ AI starts in %.0f sec", remaining))
end
end
end
end)
-- ============================================================
-- SECTION 32: SAFETY HOOKS
-- ============================================================
Players.LocalPlayer.AncestryChanged:Connect(function()
if not Players.LocalPlayer.Parent then
if AIState.isLearning and AIState.currentSession then
Observer.endSession()
end
saveBrain()
saveConfig()
end
end)
LocalPlayer.CharacterRemoving:Connect(function()
if AIState.mode ~= "idle" and AIState.mode ~= "observing" then
GhostController.reclaim()
VoidGUI.notify("Character removed — control reclaimed")
end
end)
-- ============================================================
-- SECTION 19: INITIALIZATION (FINAL)
-- ============================================================
local function init()
-- Setup folders
Exec.makeFolder(BRAIN_FOLDER)
-- Load config first
loadConfig()
-- Apply saved config
if Config.stealthMode then AIState.isStealth = true end
if Config.activePersona then AIState.persona = Config.activePersona end
if Config.keybinds then
for action, keyName in pairs(Config.keybinds) do
if KEY_MAP[keyName] then
Keybinds.binds[action] = KEY_MAP[keyName]
end
end
end
-- Load brain
loadBrain()
-- Build GUI
if not AIState.isStealth then
VoidGUI.build()
VoidGUI.updateDisplay()
else
-- Stealth: build but hide, mini button only
VoidGUI.build()
if mainFrame then mainFrame.Visible = false end
if miniButton then miniButton.Visible = true end
end
-- Build threat HUD after GUI exists
task.spawn(function()
task.wait(0.3)
buildThreatHUD()
end)
-- Run horror detection after brain loads
task.spawn(function()
task.wait(4)
detectAndConfigureHorror()
end)
-- Run sequence analysis on startup if brain already has sessions
task.spawn(function()
task.wait(3)
SequenceDetector.analyze()
end)
-- Add mental map to stats tab after GUI builds
task.spawn(function()
task.wait(0.5)
if screenGui then
local panel = screenGui:FindFirstChild("VoidAIPanel")
if panel then
local scrollFrames = {}
for _, child in ipairs(panel:GetChildren()) do
if child:IsA("ScrollingFrame") then
table.insert(scrollFrames, child)
end
end
-- Stats tab = index 2, Settings tab = index 4
local statsFrame = scrollFrames[2]
local settingsFrame = scrollFrames[4]
if statsFrame then
buildMentalMapView(statsFrame, 330)
statsFrame.CanvasSize = UDim2.new(0,0,0,460)
end
if settingsFrame then
local exportBtn = makeButton(settingsFrame,
"📤 Export Brain Summary",
UDim2.new(1,-12,0,28),
UDim2.new(0,6,0,500),
COLORS.panel)
exportBtn.MouseButton1Click:Connect(exportBrainSummary)
settingsFrame.CanvasSize = UDim2.new(0,0,0,560)
end
end
end
end)
-- Live display update loop (every second)
task.spawn(function()
while true do
task.wait(1)
VoidGUI.updateDisplay()
end
end)
-- Welcome notification
task.wait(0.5)
if not AIState.isStealth then
local platform = isMobile and "Mobile 📱" or "PC 🖥"
local brainStatus = Exec.isFile(getBrainPath()) and "Brain loaded ✓" or "New brain created"
VoidGUI.notify("VoidAI v1.0 — " .. platform .. " — " .. brainStatus)
end
-- Print to output for executor console
print("[VoidAI] Loaded successfully")
print("[VoidAI] Platform: " .. (isMobile and "Mobile" or "PC"))
print("[VoidAI] Brain path: " .. getBrainPath())
print("[VoidAI] Universe: " .. getUniverseId() .. " | Place: " .. getPlaceId())
print("[VoidAI] Chat commands: /void help")
if isPC then
print("[VoidAI] Keybinds: G=Learn H=Lend J=GUI F=Reclaim")
end
end
-- ============================================================
-- SECTION 33: ADAPTIVE THINKING MODE
-- ============================================================
--[[
Adaptive Thinking Mode (ATM):
- Activates automatically when player is AFK (no input for N seconds)
- OR manually toggled from GUI / chat command
- AI watches ALL other players in the server simultaneously
- Learns their movement paths, sequences, clustering patterns
- Cross-references: 3+ players took same path = high-confidence route
- Tags player clusters as "safe", player flee-zones as "danger"
- All observed data is weighted at 60% trust vs 100% for self-recorded
- Stops instantly the moment user touches any input
- Brain data from ATM is clearly marked so you can see what came from it
]]
local AdaptiveThinking = {}
-- State
AdaptiveThinking.active = false
AdaptiveThinking.manualOverride= false -- user forced it on via GUI
AdaptiveThinking.afkTimer = 0
AdaptiveThinking.afkThreshold = 30 -- seconds of no input = AFK
AdaptiveThinking.TRUST_WEIGHT = 0.6 -- observed data trust vs 1.0 for self
AdaptiveThinking.tickAccum = 0
AdaptiveThinking.sampleRate = 0.4 -- seconds between player samples (adaptive)
AdaptiveThinking.sessionStart = 0
-- Per-player tracking table: stores last known positions + action hints
AdaptiveThinking.playerTrackers = {}
-- Observed session accumulator (separate from self-recorded sessions)
AdaptiveThinking.observedSession = nil
-- Path frequency map: posKey -> count across all observed players
AdaptiveThinking.pathFrequency = {}
-- ---- AFK DETECTION ----
local lastInputTime = os.time()
if not isMobile then
UserInputService.InputBegan:Connect(function(input, processed)
-- Any real input = not AFK
local t = input.UserInputType
if t == Enum.UserInputType.Keyboard
or t == Enum.UserInputType.MouseButton1
or t == Enum.UserInputType.MouseButton2
or t == Enum.UserInputType.MouseMovement then
lastInputTime = os.time()
-- If ATM was running in auto mode, stop it
if AdaptiveThinking.active and not AdaptiveThinking.manualOverride then
AdaptiveThinking.stop("Input detected — AFK mode off")
end
end
end)
UserInputService.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
lastInputTime = os.time()
if AdaptiveThinking.active and not AdaptiveThinking.manualOverride then
AdaptiveThinking.stop("Mouse moved — AFK mode off")
end
end
end)
else
-- Mobile: any touch = not AFK
UserInputService.TouchStarted:Connect(function()
lastInputTime = os.time()
if AdaptiveThinking.active and not AdaptiveThinking.manualOverride then
AdaptiveThinking.stop("Touch detected — AFK mode off")
end
end)
end
-- ---- CORE FUNCTIONS ----
function AdaptiveThinking.start(reason)
if AdaptiveThinking.active then return end
AdaptiveThinking.active = true
AdaptiveThinking.sessionStart = os.time()
AdaptiveThinking.pathFrequency= {}
AdaptiveThinking.playerTrackers = {}
-- Create a fresh observed session
AdaptiveThinking.observedSession = {
id = os.time(),
type = "adaptive_observed", -- marked as ATM data
trustWeight = AdaptiveThinking.TRUST_WEIGHT,
actions = {},
movements = {},
sequences = {},
playerPaths = {}, -- per-player path logs
startTime = os.time(),
endTime = nil,
playerCount = #Players:GetPlayers() - 1, -- exclude self
source = "adaptive_thinking",
}
VoidGUI.notify("🧠 Adaptive Thinking ON — watching " ..
tostring(#Players:GetPlayers() - 1) .. " players")
print("[VoidAI:ATM] Started — reason: " .. (reason or "manual"))
end
function AdaptiveThinking.stop(reason)
if not AdaptiveThinking.active then return end
AdaptiveThinking.active = false
AdaptiveThinking.manualOverride= false
-- Finalize and merge observed session into brain
if AdaptiveThinking.observedSession then
AdaptiveThinking.observedSession.endTime = os.time()
AdaptiveThinking.mergeIntoBrain()
end
AdaptiveThinking.observedSession = nil
VoidGUI.notify("🧠 Adaptive Thinking OFF — brain updated")
print("[VoidAI:ATM] Stopped — reason: " .. (reason or "manual"))
VoidGUI.updateDisplay()
end
function AdaptiveThinking.toggle()
if AdaptiveThinking.active then
AdaptiveThinking.manualOverride = false
AdaptiveThinking.stop("manual toggle")
else
AdaptiveThinking.manualOverride = true
AdaptiveThinking.start("manual toggle")
end
end
-- ---- PLAYER SAMPLING ----
function AdaptiveThinking.samplePlayers()
if not AdaptiveThinking.active then return end
local place = getCurrentPlace()
if not place then return end
local session = AdaptiveThinking.observedSession
if not session then return end
local allPlayers = Players:GetPlayers()
local activeSamples = 0
for _, p in ipairs(allPlayers) do
if p == LocalPlayer then continue end
if not p.Character then continue end
local hrp = p.Character:FindFirstChild("HumanoidRootPart")
local hum = p.Character:FindFirstChildOfClass("Humanoid")
if not hrp or not hum then continue end
local pos = hrp.Position
local pid = p.UserId
activeSamples += 1
-- Initialize tracker for this player
if not AdaptiveThinking.playerTrackers[pid] then
AdaptiveThinking.playerTrackers[pid] = {
name = p.Name,
lastPos = pos,
lastMoveT = os.time(),
pathPoints = {},
isRunning = false,
fleeCount = 0,
}
session.playerPaths[tostring(pid)] = {
name = p.Name,
points = {},
}
end
local tracker = AdaptiveThinking.playerTrackers[pid]
local delta = (pos - tracker.lastPos).Magnitude
-- Only record if player actually moved (>2 studs, same threshold as self-observer)
if delta >= 2 then
tracker.lastPos = pos
tracker.lastMoveT= os.time()
-- Log point to this player's path
local point = {
pos = {x = pos.X, y = pos.Y, z = pos.Z},
t = os.time() - AdaptiveThinking.sessionStart,
spd = hum.WalkSpeed,
}
table.insert(tracker.pathPoints, point)
if session.playerPaths[tostring(pid)] then
table.insert(session.playerPaths[tostring(pid)].points, point)
end
-- Update path frequency map for this grid cell
local posKey = math.floor(pos.X/10) .. "_" .. math.floor(pos.Z/10)
AdaptiveThinking.pathFrequency[posKey] =
(AdaptiveThinking.pathFrequency[posKey] or 0) + 1
-- Add to observed session as a weighted movement action
table.insert(session.actions, {
type = "move",
source = "observed_" .. p.Name,
trustWeight = AdaptiveThinking.TRUST_WEIGHT,
data = point,
})
table.insert(session.movements, point)
end
-- Detect fleeing (high walkspeed = running)
local isFleeing = hum.WalkSpeed > 20
if isFleeing and not tracker.isRunning then
tracker.isRunning = true
tracker.fleeCount += 1
-- Record flee event
table.insert(session.actions, {
type = "flee_observed",
source = p.Name,
data = {
pos = {x = pos.X, y = pos.Y, z = pos.Z},
t = os.time() - AdaptiveThinking.sessionStart,
}
})
-- If 3+ players flee from roughly same area = strong danger signal
local nearbyFleeing = 0
for _, p2 in ipairs(allPlayers) do
if p2 ~= LocalPlayer and p2 ~= p and p2.Character then
local hrp2 = p2.Character:FindFirstChild("HumanoidRootPart")
local hum2 = p2.Character:FindFirstChildOfClass("Humanoid")
if hrp2 and hum2 and hum2.WalkSpeed > 20 then
if (hrp2.Position - pos).Magnitude < 60 then
nearbyFleeing += 1
end
end
end
end
if nearbyFleeing >= 2 then
-- Tag flee origin as danger
local fleeKey = math.floor(pos.X/10) .. "_" .. math.floor(pos.Z/10)
place.mentalMap.landmarks[fleeKey] = {
pos = {x = pos.X, y = pos.Y, z = pos.Z},
tag = "danger",
visits = nearbyFleeing,
note = "atm:mass_flee",
}
print("[VoidAI:ATM] Mass flee detected — tagged danger at " .. fleeKey)
end
elseif not isFleeing then
tracker.isRunning = false
end
end
-- Update sample rate based on player count (more players = slower sample to avoid lag)
if activeSamples > 10 then
AdaptiveThinking.sampleRate = 0.8
elseif activeSamples > 5 then
AdaptiveThinking.sampleRate = 0.5
else
AdaptiveThinking.sampleRate = 0.3
end
end
-- ---- CROSS-REFERENCE & ZONE TAGGING ----
function AdaptiveThinking.processPathFrequency()
if not AdaptiveThinking.active then return end
local place = getCurrentPlace()
if not place then return end
for posKey, count in pairs(AdaptiveThinking.pathFrequency) do
local parts = posKey:split("_")
if #parts >= 2 then
local wx = tonumber(parts[1]) * 10
local wz = tonumber(parts[2]) * 10
if not place.mentalMap.landmarks[posKey] then
place.mentalMap.landmarks[posKey] = {
pos = {x = wx, y = 0, z = wz},
tag = "visited",
visits = 0,
note = "atm",
}
end
local lm = place.mentalMap.landmarks[posKey]
-- Increment visits by weighted count
lm.visits = (lm.visits or 0) + math.floor(count * AdaptiveThinking.TRUST_WEIGHT)
-- Cross-reference: 3+ distinct players passed here = likely safe/important route
if count >= 3 and lm.tag == "visited" then
lm.tag = "safe"
lm.note = "atm:high_traffic"
end
end
end
end
-- ---- MERGE INTO BRAIN ----
function AdaptiveThinking.mergeIntoBrain()
local place = getCurrentPlace()
if not place or not AdaptiveThinking.observedSession then return end
local session = AdaptiveThinking.observedSession
-- Process path frequency → zone tags
AdaptiveThinking.processPathFrequency()
-- Only add observed session if it has meaningful data
local actionCount = #session.actions
if actionCount < 10 then
print("[VoidAI:ATM] Observed session too sparse (" .. actionCount .. " actions) — discarded")
return
end
-- Add to place sessions with trust weight metadata
table.insert(place.sessions, session)
place.totalActions = (place.totalActions or 0) + actionCount
-- Recalculate confidence, but dampen ATM contribution
-- ATM sessions count as 0.6x a normal session for confidence
local atmSessions = 0
local selfSessions = 0
for _, s in ipairs(place.sessions) do
if s.type == "adaptive_observed" then
atmSessions += 1
else
selfSessions += 1
end
end
-- Weighted confidence: self sessions worth 1.0, ATM sessions worth 0.6
local weightedCount = selfSessions + atmSessions * AdaptiveThinking.TRUST_WEIGHT
if weightedCount > 0 then
local totalMoves, totalUI, totalSeq = 0, 0, 0
for _, s in ipairs(place.sessions) do
local w = (s.type == "adaptive_observed") and AdaptiveThinking.TRUST_WEIGHT or 1.0
totalMoves = totalMoves + #(s.movements or {}) * w
totalUI = totalUI + #(s.uiClicks or {}) * w
totalSeq = totalSeq + #(s.sequences or {}) * w
end
place.confidence.movement = math.min(100, math.floor(math.log(totalMoves + 1) * 15))
place.confidence.ui = math.min(100, math.floor(math.log(totalUI + 1) * 20))
place.confidence.sequences = math.min(100, math.floor(math.log(totalSeq + 1) * 25))
end
-- Run sequence analysis on the new data
SequenceDetector.analyze()
saveBrain()
print(string.format("[VoidAI:ATM] Merged — %d actions from %d players | Conf: Move%d%% UI%d%%",
actionCount,
session.playerCount,
place.confidence.movement,
place.confidence.ui))
end
-- ---- MAIN HEARTBEAT HOOK ----
RunService.Heartbeat:Connect(function(dt)
-- AFK timer (only auto-trigger if not manual override)
if not AdaptiveThinking.manualOverride then
local idleTime = os.time() - lastInputTime
if idleTime >= AdaptiveThinking.afkThreshold and not AdaptiveThinking.active then
-- Auto-start ATM if player appears to be AFK
-- Only if not already in another active AI mode
if AIState.mode == "idle" or AIState.mode == "observing" then
AdaptiveThinking.start("AFK auto-trigger (" .. idleTime .. "s idle)")
end
end
end
-- Sample players while ATM is active
if AdaptiveThinking.active then
AdaptiveThinking.tickAccum += dt
if AdaptiveThinking.tickAccum >= AdaptiveThinking.sampleRate then
AdaptiveThinking.tickAccum = 0
AdaptiveThinking.samplePlayers()
-- Process path frequency every 15 seconds
local elapsed = os.time() - AdaptiveThinking.sessionStart
if elapsed > 0 and elapsed % 15 == 0 then
AdaptiveThinking.processPathFrequency()
end
end
end
end)
-- ---- GUI INTEGRATION ----
-- Add ATM button and status to main tab after GUI builds
task.spawn(function()
task.wait(1)
if not screenGui then return end
local panel = screenGui:FindFirstChild("VoidAIPanel")
if not panel then return end
-- Find main tab ScrollingFrame (first one)
local scrollFrames = {}
for _, child in ipairs(panel:GetChildren()) do
if child:IsA("ScrollingFrame") then
table.insert(scrollFrames, child)
end
end
local mainScroll = scrollFrames[1]
if not mainScroll then return end
-- ATM toggle button
local atmBtn = makeButton(mainScroll,
"🧠 Adaptive Thinking: OFF",
UDim2.new(1,-12,0,28),
UDim2.new(0,6,0,310),
COLORS.panel)
atmBtn.TextSize = 11
-- AFK timer label
local atmStatus = makeLabel(mainScroll,
"AFK: 0s / 30s — 0 players watched",
UDim2.new(1,-12,0,14),
UDim2.new(0,6,0,342),
9, COLORS.textDim)
atmBtn.MouseButton1Click:Connect(function()
AdaptiveThinking.toggle()
if AdaptiveThinking.active then
atmBtn.Text = "🧠 Adaptive Thinking: ON"
atmBtn.BackgroundColor3 = COLORS.accent
else
atmBtn.Text = "🧠 Adaptive Thinking: OFF"
atmBtn.BackgroundColor3 = COLORS.panel
end
end)
-- Live status updater for ATM button
task.spawn(function()
while true do
task.wait(1)
local idleTime = os.time() - lastInputTime
local pCount = math.max(0, #Players:GetPlayers() - 1)
if AdaptiveThinking.active then
local elapsed = os.time() - AdaptiveThinking.sessionStart
local actions = AdaptiveThinking.observedSession
and #AdaptiveThinking.observedSession.actions or 0
atmStatus.Text = string.format(
"ATM: %ds active | %d players | %d actions learned",
elapsed, pCount, actions)
atmStatus.TextColor3 = COLORS.success
atmBtn.Text = "🧠 Adaptive Thinking: ON"
atmBtn.BackgroundColor3 = COLORS.accent
else
local threshold = AdaptiveThinking.afkThreshold
local remaining = math.max(0, threshold - idleTime)
atmStatus.Text = string.format(
"AFK: %ds idle | auto-starts in %ds | %d players",
math.floor(idleTime), math.floor(remaining), pCount)
atmStatus.TextColor3 = COLORS.textDim
atmBtn.Text = "🧠 Adaptive Thinking: OFF"
atmBtn.BackgroundColor3 = COLORS.panel
end
end
end)
-- Expand canvas to fit new button
mainScroll.CanvasSize = UDim2.new(0,0,0,580)
end)
-- ---- CHAT COMMAND ----
CHAT_COMMANDS["atm"] = function()
AdaptiveThinking.toggle()
VoidGUI.notify(AdaptiveThinking.active and
"🧠 ATM ON — watching players" or
"🧠 ATM OFF — brain saved")
end
CHAT_COMMANDS["afk"] = function()
-- Set AFK threshold to 0 to force immediate ATM trigger
lastInputTime = 0
VoidGUI.notify("AFK forced — ATM will start shortly")
end
-- ---- STATS TAB INTEGRATION ----
-- Patch stats display to show ATM session count separately
local _origBuildStats = VoidGUI.buildStatsTab
function VoidGUI.buildStatsTab(container)
_origBuildStats(container)
-- Add ATM-specific stat rows
local place = getCurrentPlace()
if not place then return end
local atmCount = 0
local selfCount = 0
for _, s in ipairs(place.sessions) do
if s.type == "adaptive_observed" then
atmCount += 1
else
selfCount += 1
end
end
makeLabel(container,
"Self Sessions",
UDim2.new(0.6,0,0,16),
UDim2.new(0,6,0,195), 11, COLORS.textDim)
makeLabel(container,
tostring(selfCount),
UDim2.new(0.4,0,0,16),
UDim2.new(0.6,0,0,195), 11, COLORS.success)
makeLabel(container,
"ATM Sessions (0.6x)",
UDim2.new(0.6,0,0,16),
UDim2.new(0,6,0,213), 11, COLORS.textDim)
makeLabel(container,
tostring(atmCount),
UDim2.new(0.4,0,0,16),
UDim2.new(0.6,0,0,213), 11, COLORS.accent)
end
-- ============================================================
-- SECTION 34: EXTENDED ADAPTIVE THINKING (EAT)
-- ============================================================
--[[
Extended Adaptive Thinking (EAT):
Runs while the USER plays normally — no lending, no control taken.
Learns from BOTH the user's own actions AND other players simultaneously.
Layered with AI-mimicking systems:
- Pattern Reinforcement (dual-source confirmation)
- Attention Focus (prioritize active players)
- Action Chunking / Macro system
- Forgetting Curve (session time decay)
- Next-Action Prediction Engine
- Working Memory Buffer (short-term vs long-term)
- Behavior Clustering (path generalization)
]]
local EAT = {}
-- ---- STATE ----
EAT.active = false
EAT.sessionStart = 0
EAT.tickAccum = 0
EAT.sampleRate = 0.25 -- base sample rate, adapts by FPS + player count
-- ---- WORKING MEMORY BUFFER (short-term) ----
-- Holds last N actions seen by user. Checked before long-term brain.
EAT.workingMemory = {} -- circular buffer
EAT.WM_CAPACITY = 20 -- max entries
-- ---- MACRO / CHUNK SYSTEM ----
EAT.macros = {} -- named action chunks
EAT.currentChunk = {} -- current chunk being built
EAT.CHUNK_MIN_LEN = 3 -- minimum actions to form a chunk
EAT.CHUNK_MAX_LEN = 8
EAT.chunkLabels = {
["move|move|move|ui_click"] = "approach_and_interact",
["sprint_start|move|move|jump"] = "parkour_sequence",
["move|ui_click|move|move"] = "click_and_move",
["jump|move|move|move"] = "jump_traverse",
["sprint_start|move|move|move"] = "sprint_path",
["ui_click|ui_click|move"] = "multi_interact",
["move|move|jump|move|move"] = "gap_cross",
["ui_click|move|ui_click"] = "ui_navigate",
["move|move|move|move|move"] = "long_path",
["sprint_start|sprint_end|ui_click"] = "sprint_to_objective",
}
-- ---- ATTENTION SCORER ----
-- Each player gets a score 0-100 representing how worth-watching they are
EAT.attentionScores = {} -- [userId] = score
EAT.attentionHistory= {} -- [userId] = {lastPos, lastActionCount, lastUpdateTime}
-- ---- FORGETTING CURVE ----
-- Returns weight multiplier [0.0 - 1.0] based on session age in seconds
local FORGETTING_CURVE = {
{days=0, weight=1.00},
{days=3, weight=0.80},
{days=7, weight=0.60},
{days=14, weight=0.30},
{days=30, weight=0.10},
{days=999, weight=0.05},
}
local SECONDS_PER_DAY = 86400
function EAT.getTimeDecayWeight(sessionTimestamp)
if not sessionTimestamp then return 1.0 end
local ageSeconds = os.time() - sessionTimestamp
local ageDays = ageSeconds / SECONDS_PER_DAY
local weight = 0.05
for i = 1, #FORGETTING_CURVE - 1 do
local a = FORGETTING_CURVE[i]
local b = FORGETTING_CURVE[i + 1]
if ageDays >= a.days and ageDays < b.days then
-- Linear interpolation between curve points
local t = (ageDays - a.days) / (b.days - a.days)
weight = a.weight + (b.weight - a.weight) * t
break
end
end
return math.max(0.05, weight)
end
-- ---- PATTERN REINFORCEMENT ----
-- Tracks which sequences were seen from BOTH self AND observed players
EAT.reinforcedPatterns = {} -- sequenceKey -> {selfCount, observedCount, totalWeight}
function EAT.reinforcePattern(seqKey, source)
if not EAT.reinforcedPatterns[seqKey] then
EAT.reinforcedPatterns[seqKey] = {
selfCount = 0,
observedCount = 0,
totalWeight = 0,
}
end
local pat = EAT.reinforcedPatterns[seqKey]
if source == "self" then
pat.selfCount += 1
else
pat.observedCount += 1
end
-- Weight doubles when BOTH self and observed confirm it
if pat.selfCount > 0 and pat.observedCount > 0 then
pat.totalWeight = (pat.selfCount + pat.observedCount) * 2.0 -- dual-source bonus
else
pat.totalWeight = pat.selfCount * 1.0 + pat.observedCount * 0.6
end
end
function EAT.getPatternWeight(seqKey)
local pat = EAT.reinforcedPatterns[seqKey]
if not pat then return 0 end
return pat.totalWeight
end
-- ---- WORKING MEMORY ----
function EAT.pushWorkingMemory(action)
table.insert(EAT.workingMemory, action)
if #EAT.workingMemory > EAT.WM_CAPACITY then
table.remove(EAT.workingMemory, 1) -- drop oldest
end
end
function EAT.getWorkingMemoryKey()
-- Builds a sequence key from recent working memory entries
local key = ""
local start = math.max(1, #EAT.workingMemory - 4)
for i = start, #EAT.workingMemory do
key = key .. (EAT.workingMemory[i].type or "?") .. "|"
end
return key
end
-- ---- ACTION CHUNKING ----
function EAT.pushChunk(actionType)
table.insert(EAT.currentChunk, actionType)
if #EAT.currentChunk > EAT.CHUNK_MAX_LEN then
table.remove(EAT.currentChunk, 1)
end
-- Check if current chunk matches any label
EAT.detectMacro()
end
function EAT.detectMacro()
if #EAT.currentChunk < EAT.CHUNK_MIN_LEN then return nil end
-- Try matching from longest to shortest window
for len = math.min(#EAT.currentChunk, EAT.CHUNK_MAX_LEN), EAT.CHUNK_MIN_LEN, -1 do
local key = ""
local startIdx = #EAT.currentChunk - len + 1
for i = startIdx, #EAT.currentChunk do
key = key .. EAT.currentChunk[i] .. "|"
end
-- Remove trailing |
key = key:sub(1, -2)
local label = EAT.chunkLabels[key]
if label then
-- Register or reinforce this macro
if not EAT.macros[label] then
EAT.macros[label] = {
label = label,
key = key,
count = 0,
actions = {},
lastSeen = 0,
}
end
EAT.macros[label].count += 1
EAT.macros[label].lastSeen = os.time()
-- Save actual action data snapshot for replay
local place = getCurrentPlace()
if place and AIState.currentSession then
local total = #AIState.currentSession.actions
local slice = {}
for i = math.max(1, total - len + 1), total do
table.insert(slice, AIState.currentSession.actions[i])
end
EAT.macros[label].actions = slice
end
print("[VoidAI:EAT] Macro detected: " .. label .. " (x" .. EAT.macros[label].count .. ")")
return label
end
end
return nil
end
function EAT.saveMacros()
-- Persist macros into brain file
local place = getCurrentPlace()
if not place then return end
place.macros = EAT.macros
end
function EAT.loadMacros()
local place = getCurrentPlace()
if not place then return end
if place.macros then
EAT.macros = place.macros
local count = 0
for _ in pairs(EAT.macros) do count += 1 end
if count > 0 then
print("[VoidAI:EAT] Loaded " .. count .. " macros from brain")
end
end
end
-- ---- ATTENTION FOCUS ----
function EAT.updateAttentionScore(player)
local pid = player.UserId
local char = player.Character
if not char then return 0 end
local hrp = char:FindFirstChild("HumanoidRootPart")
local hum = char:FindFirstChildOfClass("Humanoid")
if not hrp or not hum then return 0 end
if not EAT.attentionHistory[pid] then
EAT.attentionHistory[pid] = {
lastPos = hrp.Position,
lastActionCount = 0,
lastUpdateTime = os.time(),
score = 50,
}
end
local hist = EAT.attentionHistory[pid]
local timeDelta= os.time() - hist.lastUpdateTime
if timeDelta < 0.5 then return hist.score end
local score = 0
-- Movement activity (0-40 pts)
local moveDelta = (hrp.Position - hist.lastPos).Magnitude
score += math.min(40, moveDelta * 2)
-- Walk speed (running = interesting, 0-20 pts)
if hum.WalkSpeed > 20 then score += 20
elseif hum.WalkSpeed > 14 then score += 10 end
-- Health change detection (combat/damage = interesting, 0-20 pts)
if hum.Health < hum.MaxHealth * 0.9 then score += 20
elseif hum.Health < hum.MaxHealth then score += 10 end
-- Idle penalty
if moveDelta < 1 and hum.WalkSpeed <= 0 then
score = math.max(0, score - 30)
end
-- Smooth score with exponential moving average
hist.score = hist.score * 0.7 + score * 0.3
hist.lastPos = hrp.Position
hist.lastUpdateTime= os.time()
EAT.attentionScores[pid] = hist.score
return hist.score
end
function EAT.getSortedPlayers()
-- Returns players sorted by attention score descending
local scored = {}
for _, p in ipairs(Players:GetPlayers()) do
if p ~= LocalPlayer then
local score = EAT.updateAttentionScore(p)
table.insert(scored, {player = p, score = score})
end
end
table.sort(scored, function(a, b) return a.score > b.score end)
return scored
end
-- ---- BEHAVIOR CLUSTERING ----
-- Groups similar movement positions into cluster centroids
-- So AI generalizes paths instead of memorizing exact coordinates
EAT.clusters = {} -- list of {centroid, members, tag}
EAT.CLUSTER_RADIUS = 15 -- studs — positions within this distance merge
function EAT.addToCluster(pos, tag)
-- Find nearest existing cluster
local nearest = nil
local nearestDist= math.huge
for i, cluster in ipairs(EAT.clusters) do
local c = cluster.centroid
local dist= (Vector3.new(c.x,c.y,c.z) - pos).Magnitude
if dist < nearestDist then
nearestDist = dist
nearest = i
end
end
if nearest and nearestDist <= EAT.CLUSTER_RADIUS then
-- Merge into existing cluster — update centroid
local cluster = EAT.clusters[nearest]
local n = #cluster.members + 1
cluster.centroid = {
x = (cluster.centroid.x * (n-1) + pos.X) / n,
y = (cluster.centroid.y * (n-1) + pos.Y) / n,
z = (cluster.centroid.z * (n-1) + pos.Z) / n,
}
table.insert(cluster.members, {x=pos.X, y=pos.Y, z=pos.Z})
if tag and tag ~= "visited" then cluster.tag = tag end
else
-- Create new cluster
table.insert(EAT.clusters, {
centroid = {x=pos.X, y=pos.Y, z=pos.Z},
members = {{x=pos.X, y=pos.Y, z=pos.Z}},
tag = tag or "visited",
weight = 1,
})
end
end
function EAT.getNearestCluster(pos)
local nearest = nil
local nearestDist= math.huge
for _, cluster in ipairs(EAT.clusters) do
local c = cluster.centroid
local dist= (Vector3.new(c.x,c.y,c.z) - pos).Magnitude
if dist < nearestDist then
nearestDist = dist
nearest = cluster
end
end
return nearest, nearestDist
end
function EAT.saveClusters()
local place = getCurrentPlace()
if not place then return end
-- Cap at 500 clusters to avoid bloat
if #EAT.clusters > 500 then
-- Keep only the most-membered clusters
table.sort(EAT.clusters, function(a,b)
return #a.members > #b.members
end)
local trimmed = {}
for i = 1, 500 do trimmed[i] = EAT.clusters[i] end
EAT.clusters = trimmed
end
place.clusters = EAT.clusters
end
function EAT.loadClusters()
local place = getCurrentPlace()
if not place then return end
if place.clusters then
EAT.clusters = place.clusters
print("[VoidAI:EAT] Loaded " .. #EAT.clusters .. " behavior clusters")
end
end
-- ---- NEXT-ACTION PREDICTION ENGINE ----
-- Scores candidate actions based on:
-- 1. Working memory context (recent actions)
-- 2. Reinforced pattern weights
-- 3. Cluster proximity
-- 4. Forgetting-curve-weighted session history
EAT.lastPrediction = nil
EAT.predictionScores= {}
local CANDIDATE_ACTIONS = {
"move", "jump", "sprint_start", "ui_click",
"hide", "flee", "interact", "wait",
}
function EAT.predictNextAction()
local place = getCurrentPlace()
if not place then return nil end
local wmKey = EAT.getWorkingMemoryKey()
local scores = {}
for _, candidate in ipairs(CANDIDATE_ACTIONS) do
local score = 0
-- 1. Working memory match: does recent history predict this next?
local testKey = wmKey .. candidate
for seqKey, pat in pairs(EAT.reinforcedPatterns) do
if seqKey:find(testKey, 1, true) then
score += EAT.getPatternWeight(seqKey) * 10
end
end
-- 2. Session history: how often does this action follow current context?
for _, session in ipairs(place.sessions) do
local decay = EAT.getTimeDecayWeight(session.startTime or session.id)
local trust = (session.type == "adaptive_observed") and 0.6 or 1.0
local actions= session.actions or {}
for i = 2, #actions do
if actions[i-1].type == (EAT.workingMemory[#EAT.workingMemory] or {}).type then
if actions[i].type == candidate then
score += decay * trust * 5
end
end
end
end
-- 3. Threat modifier: if threat detected, boost survival actions
if AIState.threatDetected then
if candidate == "hide" or candidate == "flee" then
score += 50
end
if candidate == "ui_click" or candidate == "wait" then
score -= 20
end
end
-- 4. Persona modifier
if AIState.persona == "Aggressive" then
if candidate == "sprint_start" or candidate == "move" then score += 10 end
if candidate == "hide" or candidate == "wait" then score -= 15 end
elseif AIState.persona == "Passive" then
if candidate == "wait" or candidate == "ui_click" then score += 10 end
if candidate == "sprint_start" then score -= 10 end
end
scores[candidate] = math.max(0, score)
end
-- Find highest scoring candidate
local bestAction = "move"
local bestScore = -1
for action, score in pairs(scores) do
if score > bestScore then
bestScore = score
bestAction = action
end
end
EAT.lastPrediction = bestAction
EAT.predictionScores = scores
return bestAction, bestScore, scores
end
-- ---- FORGETTING CURVE APPLICATION ----
-- Recalculates confidence using time-decayed session weights
-- Called periodically so confidence reflects recent relevance
function EAT.applyForgettingCurve()
local place = getCurrentPlace()
if not place or #place.sessions == 0 then return end
local totalMoves, totalUI, totalSeq = 0, 0, 0
for _, session in ipairs(place.sessions) do
local decay = EAT.getTimeDecayWeight(session.startTime or session.id)
local trust = (session.type == "adaptive_observed")
and AdaptiveThinking.TRUST_WEIGHT or 1.0
local w = decay * trust
totalMoves = totalMoves + #(session.movements or {}) * w
totalUI = totalUI + #(session.uiClicks or {}) * w
totalSeq = totalSeq + #(session.sequences or {}) * w
end
place.confidence.movement = math.min(100, math.floor(math.log(totalMoves + 1) * 15))
place.confidence.ui = math.min(100, math.floor(math.log(totalUI + 1) * 20))
place.confidence.sequences = math.min(100, math.floor(math.log(totalSeq + 1) * 25))
end
-- ---- CORE: OBSERVE SELF TICK ----
-- Runs while EAT is active AND user is actively playing
function EAT.observeSelf()
local char = LocalPlayer.Character
if not char then return end
local hrp = char:FindFirstChild("HumanoidRootPart")
local hum = char:FindFirstChildOfClass("Humanoid")
if not hrp or not hum then return end
local pos = hrp.Position
-- Record movement to working memory
local moveDelta = (pos - Observer.lastPosition).Magnitude
if moveDelta >= 2 then
local action = {
type = "move",
source = "self",
data = {pos={x=pos.X,y=pos.Y,z=pos.Z}, t=os.time()-AIState.sessionStart},
}
EAT.pushWorkingMemory(action)
EAT.pushChunk("move")
-- Add to behavior clusters
EAT.addToCluster(pos, "visited")
-- Reinforce this as a self-observed pattern
local wmKey = EAT.getWorkingMemoryKey()
EAT.reinforcePattern(wmKey, "self")
end
end
-- ---- CORE: OBSERVE OTHERS TICK ----
-- Attention-weighted player sampling
function EAT.observeOthers()
local place = getCurrentPlace()
if not place then return end
local sortedPlayers = EAT.getSortedPlayers()
-- Top 30% of players get 3x more samples, bottom 20% are skipped
local total = #sortedPlayers
if total == 0 then return end
for rank, entry in ipairs(sortedPlayers) do
local p = entry.player
local score = entry.score
-- Skip idle players entirely (score < 15)
if score < 15 then continue end
-- Sub-sample low-attention players (50% chance)
local normalized = rank / total -- 0=highest attention, 1=lowest
if normalized > 0.7 then
if math.random() > 0.5 then continue end
end
local char = p.Character
if not char then continue end
local hrp = char:FindFirstChild("HumanoidRootPart")
local hum = char:FindFirstChildOfClass("Humanoid")
if not hrp or not hum then continue end
local pos = hrp.Position
-- Track this player's previous position
local pid = p.UserId
if not AdaptiveThinking.playerTrackers[pid] then
AdaptiveThinking.playerTrackers[pid] = {
name = p.Name,
lastPos = pos,
lastMoveT = os.time(),
pathPoints= {},
isRunning = false,
fleeCount = 0,
}
end
local tracker = AdaptiveThinking.playerTrackers[pid]
local moveDelta= (pos - tracker.lastPos).Magnitude
if moveDelta < 2 then continue end
tracker.lastPos = pos
-- Build action from observation
local observedAction = {
type = "move",
source = "observed_" .. p.Name,
trustWeight = AdaptiveThinking.TRUST_WEIGHT,
data = {
pos = {x=pos.X, y=pos.Y, z=pos.Z},
t = os.time() - EAT.sessionStart,
spd = hum.WalkSpeed,
},
}
-- Push to working memory with lower trust
EAT.pushWorkingMemory(observedAction)
-- Pattern reinforcement — observed source
local wmKey = EAT.getWorkingMemoryKey()
EAT.reinforcePattern(wmKey, "observed")
-- Add to clusters
EAT.addToCluster(pos, "visited")
-- Detect fleeing
if hum.WalkSpeed > 20 and not tracker.isRunning then
tracker.isRunning = true
EAT.addToCluster(pos, "danger")
elseif hum.WalkSpeed <= 16 then
tracker.isRunning = false
end
-- Check if same path was also taken by self recently
-- If so, reinforce double
local selfNearby = false
if LocalPlayer.Character then
local selfHRP = LocalPlayer.Character:FindFirstChild("HumanoidRootPart")
if selfHRP and (selfHRP.Position - pos).Magnitude < EAT.CLUSTER_RADIUS then
selfNearby = true
end
end
if selfNearby then
EAT.reinforcePattern(wmKey, "self") -- dual-source reinforce
end
end
end
-- ---- MAIN EAT TICK (plugged into RunService) ----
RunService.Heartbeat:Connect(function(dt)
if not EAT.active then return end
EAT.tickAccum += dt
-- Adapt sample rate same as ATM
local playerCount = #Players:GetPlayers() - 1
if FPS > 50 and playerCount <= 5 then
EAT.sampleRate = 0.2
elseif FPS > 30 then
EAT.sampleRate = 0.35
else
EAT.sampleRate = 0.6
end
if EAT.tickAccum < EAT.sampleRate then return end
EAT.tickAccum = 0
-- Observe self
EAT.observeSelf()
-- Observe others
EAT.observeOthers()
-- Run prediction every 2 seconds (low priority)
if AIState.tickCounter % 8 == 0 then
EAT.predictNextAction()
end
-- Apply forgetting curve every 30 seconds
if AIState.tickCounter % 120 == 0 then
EAT.applyForgettingCurve()
end
-- Auto-save macros + clusters every 60 seconds
if AIState.tickCounter % 240 == 0 then
EAT.saveMacros()
EAT.saveClusters()
saveBrain()
end
end)
-- ---- PUBLIC API ----
function EAT.start()
if EAT.active then return end
EAT.active = true
EAT.sessionStart = os.time()
EAT.workingMemory = {}
EAT.currentChunk = {}
AdaptiveThinking.playerTrackers = {}
-- Load existing macros + clusters from brain
EAT.loadMacros()
EAT.loadClusters()
-- Make sure observer session is also running (self-learning)
if not AIState.isLearning then
AIState.isLearning = true
Observer.startSession()
end
VoidGUI.notify("⚡ Extended Adaptive Thinking ON")
print("[VoidAI:EAT] Started — dual-source learning active")
end
function EAT.stop()
if not EAT.active then return end
EAT.active = false
-- Finalize
EAT.saveMacros()
EAT.saveClusters()
EAT.applyForgettingCurve()
saveBrain()
-- Print summary
local macroCount = 0
local clusterCount = #EAT.clusters
local patCount = 0
for _ in pairs(EAT.macros) do macroCount += 1 end
for _ in pairs(EAT.reinforcedPatterns) do patCount += 1 end
VoidGUI.notify(string.format("⚡ EAT OFF — %d macros | %d clusters | %d patterns",
macroCount, clusterCount, patCount))
print(string.format("[VoidAI:EAT] Stopped — Macros:%d Clusters:%d Patterns:%d",
macroCount, clusterCount, patCount))
end
function EAT.toggle()
if EAT.active then EAT.stop() else EAT.start() end
end
-- ---- GHOST/LEND INTEGRATION ----
-- GhostController checks EAT prediction before doing linear replay
local _eatWrappedStepReplay = GhostController.stepReplay
function GhostController.stepReplay()
-- If EAT prediction is available and confident, use it
if EAT.lastPrediction and EAT.predictionScores then
local bestScore = EAT.predictionScores[EAT.lastPrediction] or 0
if bestScore > 20 then -- confidence threshold
local char = LocalPlayer.Character
local hum = char and char:FindFirstChildOfClass("Humanoid")
local hrp = char and char:FindFirstChild("HumanoidRootPart")
if EAT.lastPrediction == "jump" and hum then
hum.Jump = true
EAT.pushChunk("jump")
return
elseif EAT.lastPrediction == "sprint_start" and hum then
hum.WalkSpeed = 24
EAT.pushChunk("sprint_start")
return
elseif EAT.lastPrediction == "hide" then
GhostController.runSurvivalPattern()
return
elseif EAT.lastPrediction == "flee" then
GhostController.runSurvivalPattern()
return
elseif EAT.lastPrediction == "move" then
-- Use nearest cluster centroid as target
if hrp then
local cluster, dist = EAT.getNearestCluster(hrp.Position)
if cluster and dist > 5 then
local target = Vector3.new(
cluster.centroid.x,
cluster.centroid.y,
cluster.centroid.z
)
if hum then hum:MoveTo(target) end
EAT.pushChunk("move")
return
end
end
end
end
end
-- Fallback: check macro library first
local wmKey = EAT.getWorkingMemoryKey()
for label, macro in pairs(EAT.macros) do
if macro.count >= 2 and #macro.actions > 0 then
-- Find a macro whose key is predicted by current context
if wmKey:find(macro.key:sub(1,6), 1, true) then
-- Replay macro actions
task.spawn(function()
for _, action in ipairs(macro.actions) do
if AIState.mode ~= "ghost" and AIState.mode ~= "lend" then break end
if AIState.isPaused then break end
local char = LocalPlayer.Character
local hum = char and char:FindFirstChildOfClass("Humanoid")
if action.type == "move" and action.data then
GhostController.executeMove(action.data)
elseif action.type == "ui_click" and action.data then
GhostController.executeUIClick(action.data)
elseif action.type == "jump" and hum then
hum.Jump = true
end
task.wait(0.15)
end
end)
return
end
end
end
-- Final fallback: linear replay
_eatWrappedStepReplay()
end
-- ---- GUI INTEGRATION ----
task.spawn(function()
task.wait(1.2)
if not screenGui then return end
local panel = screenGui:FindFirstChild("VoidAIPanel")
if not panel then return end
local scrollFrames = {}
for _, child in ipairs(panel:GetChildren()) do
if child:IsA("ScrollingFrame") then
table.insert(scrollFrames, child)
end
end
local mainScroll = scrollFrames[1]
local settingsScroll = scrollFrames[4]
if mainScroll then
-- EAT toggle button (below ATM button)
local eatBtn = makeButton(mainScroll,
"⚡ Ext. Adaptive Thinking: OFF",
UDim2.new(1,-12,0,28),
UDim2.new(0,6,0,356),
COLORS.panel)
eatBtn.TextSize = 11
-- Prediction display
local predLabel = makeLabel(mainScroll,
"Prediction: —",
UDim2.new(1,-12,0,14),
UDim2.new(0,6,0,388),
9, COLORS.textDim)
-- Macro count
local macroLabel = makeLabel(mainScroll,
"Macros: 0 | Clusters: 0 | Patterns: 0",
UDim2.new(1,-12,0,14),
UDim2.new(0,6,0,404),
9, COLORS.textDim)
eatBtn.MouseButton1Click:Connect(function()
EAT.toggle()
if EAT.active then
eatBtn.Text = "⚡ Ext. Adaptive Thinking: ON"
eatBtn.BackgroundColor3 = COLORS.accentHot
else
eatBtn.Text = "⚡ Ext. Adaptive Thinking: OFF"
eatBtn.BackgroundColor3 = COLORS.panel
end
end)
-- Live update loop for EAT status
task.spawn(function()
while true do
task.wait(1)
if not mainScroll or not mainScroll.Parent then break end
-- Update button state
if EAT.active then
eatBtn.Text = "⚡ Ext. Adaptive Thinking: ON"
eatBtn.BackgroundColor3 = COLORS.accentHot
else
eatBtn.Text = "⚡ Ext. Adaptive Thinking: OFF"
eatBtn.BackgroundColor3 = COLORS.panel
end
-- Update prediction label
if EAT.lastPrediction then
local score = EAT.predictionScores and
EAT.predictionScores[EAT.lastPrediction] or 0
predLabel.Text = string.format("Prediction: %s (%.0f pts)",
EAT.lastPrediction, score)
predLabel.TextColor3 = score > 20 and COLORS.success or COLORS.textDim
else
predLabel.Text = "Prediction: — (not enough data)"
predLabel.TextColor3 = COLORS.textDim
end
-- Macro / cluster / pattern counts
local macroCount = 0
local patternCount = 0
for _ in pairs(EAT.macros) do macroCount += 1 end
for _ in pairs(EAT.reinforcedPatterns) do patternCount += 1 end
macroLabel.Text = string.format(
"Macros:%d | Clusters:%d | Patterns:%d",
macroCount, #EAT.clusters, patternCount)
end
end)
mainScroll.CanvasSize = UDim2.new(0,0,0,650)
end
-- Add EAT stats to settings tab
if settingsScroll then
makeLabel(settingsScroll,
"EAT Settings",
UDim2.new(1,-12,0,14),
UDim2.new(0,6,0,540),
11, COLORS.accent).Font = Enum.Font.GothamBold
-- AFK threshold slider (text input)
makeLabel(settingsScroll,
"AFK Threshold (sec)",
UDim2.new(0.55,0,0,22),
UDim2.new(0,6,0,558),
10, COLORS.textDim)
local afkBox = Instance.new("TextBox")
afkBox.Size = UDim2.new(0.35,0,0,22)
afkBox.Position = UDim2.new(0.6,0,0,558)
afkBox.BackgroundColor3 = COLORS.panel
afkBox.TextColor3 = COLORS.text
afkBox.Text = tostring(AdaptiveThinking.afkThreshold)
afkBox.Font = Enum.Font.GothamBold
afkBox.TextSize = 11
afkBox.BorderSizePixel = 0
afkBox.Parent = settingsScroll
local afkc = Instance.new("UICorner"); afkc.CornerRadius = UDim.new(0,4); afkc.Parent = afkBox
afkBox.FocusLost:Connect(function()
local val = tonumber(afkBox.Text)
if val and val >= 5 and val <= 300 then
AdaptiveThinking.afkThreshold = val
VoidGUI.notify("AFK threshold: " .. val .. "s")
else
afkBox.Text = tostring(AdaptiveThinking.afkThreshold)
end
end)
-- Cluster radius input
makeLabel(settingsScroll,
"Cluster Radius (studs)",
UDim2.new(0.55,0,0,22),
UDim2.new(0,6,0,584),
10, COLORS.textDim)
local clusterBox = Instance.new("TextBox")
clusterBox.Size = UDim2.new(0.35,0,0,22)
clusterBox.Position = UDim2.new(0.6,0,0,584)
clusterBox.BackgroundColor3 = COLORS.panel
clusterBox.TextColor3 = COLORS.text
clusterBox.Text = tostring(EAT.CLUSTER_RADIUS)
clusterBox.Font = Enum.Font.GothamBold
clusterBox.TextSize = 11
clusterBox.BorderSizePixel = 0
clusterBox.Parent = settingsScroll
local clc = Instance.new("UICorner"); clc.CornerRadius = UDim.new(0,4); clc.Parent = clusterBox
clusterBox.FocusLost:Connect(function()
local val = tonumber(clusterBox.Text)
if val and val >= 5 and val <= 100 then
EAT.CLUSTER_RADIUS = val
else
clusterBox.Text = tostring(EAT.CLUSTER_RADIUS)
end
end)
-- Clear clusters button
local clearBtn = makeButton(settingsScroll,
"🗑 Clear Clusters",
UDim2.new(1,-12,0,26),
UDim2.new(0,6,0,610),
COLORS.danger)
clearBtn.MouseButton1Click:Connect(function()
EAT.clusters = {}
local place = getCurrentPlace()
if place then place.clusters = {} end
VoidGUI.notify("Clusters cleared")
end)
settingsScroll.CanvasSize = UDim2.new(0,0,0,660)
end
end)
-- ---- CHAT COMMANDS ----
CHAT_COMMANDS["eat"] = function() EAT.toggle() end
CHAT_COMMANDS["macros"] = function()
local count = 0
local names = {}
for label, macro in pairs(EAT.macros) do
count += 1
table.insert(names, label .. "(x" .. macro.count .. ")")
end
VoidGUI.notify("Macros (" .. count .. "): " .. table.concat(names, ", "):sub(1, 80))
end
CHAT_COMMANDS["predict"] = function()
local action, score = EAT.predictNextAction()
VoidGUI.notify("Prediction: " .. (action or "none") .. " (" .. math.floor(score or 0) .. " pts)")
end
CHAT_COMMANDS["forget"] = function()
EAT.applyForgettingCurve()
VoidGUI.notify("Forgetting curve applied — confidence recalculated")
end
-- ---- INIT HOOK ----
-- Load EAT data when brain loads (runs after main init)
task.spawn(function()
task.wait(2)
EAT.loadMacros()
EAT.loadClusters()
local macroCount = 0
for _ in pairs(EAT.macros) do macroCount += 1 end
if macroCount > 0 then
print("[VoidAI:EAT] " .. macroCount .. " macros ready | " .. #EAT.clusters .. " clusters loaded")
end
end)
-- ============================================================
-- SECTION 35: ANOMALY DETECTION
-- ============================================================
--[[
Watches for unusual events the AI hasn't seen before.
Flags them as "anomalies" and stores them separately.
Over time, anomalies that repeat get promoted to normal patterns.
Mimics how a real AI handles out-of-distribution inputs.
]]
local AnomalyDetector = {}
AnomalyDetector.anomalies = {} -- {event, count, firstSeen, lastSeen}
AnomalyDetector.PROMOTE_COUNT= 3 -- seen N times = no longer anomaly
function AnomalyDetector.check(actionType, context)
local place = getCurrentPlace()
if not place or #place.sessions < 2 then return false end
-- Check if this action type has ever appeared in any session
local seen = false
for _, session in ipairs(place.sessions) do
for _, action in ipairs(session.actions or {}) do
if action.type == actionType then
seen = true
break
end
end
if seen then break end
end
if not seen then
-- New anomaly
local key = actionType .. "|" .. (context or "")
if not AnomalyDetector.anomalies[key] then
AnomalyDetector.anomalies[key] = {
event = actionType,
context = context,
count = 0,
firstSeen = os.time(),
lastSeen = os.time(),
promoted = false,
}
end
local a = AnomalyDetector.anomalies[key]
a.count += 1
a.lastSeen = os.time()
if a.count >= AnomalyDetector.PROMOTE_COUNT and not a.promoted then
a.promoted = true
print("[VoidAI:Anomaly] Promoted to pattern: " .. key)
VoidGUI.notify("🔍 New pattern learned: " .. actionType)
end
return true
end
return false
end
function AnomalyDetector.getUnpromoted()
local list = {}
for _, a in pairs(AnomalyDetector.anomalies) do
if not a.promoted then table.insert(list, a) end
end
table.sort(list, function(a,b) return a.count > b.count end)
return list
end
-- ============================================================
-- SECTION 36: EMOTION / URGENCY STATE
-- ============================================================
--[[
The AI maintains an internal urgency level (0-100).
Low urgency = slow careful decisions.
High urgency = fast aggressive decisions.
Driven by: threat detection, player health, nearby fleeing players,
time pressure signals (timers in UI), and persona.
Mimics an emotional state layer found in modern RL agents.
]]
local UrgencySystem = {}
UrgencySystem.level = 0 -- 0 = calm, 100 = panic
UrgencySystem.history = {} -- rolling history for smoothing
UrgencySystem.MAX_HIST = 10
function UrgencySystem.update()
local rawUrgency = 0
-- Threat detected = massive urgency spike
if AIState.threatDetected then
rawUrgency += 60
end
-- Player health
local char = LocalPlayer.Character
if char then
local hum = char:FindFirstChildOfClass("Humanoid")
if hum and hum.MaxHealth > 0 then
local healthRatio = hum.Health / hum.MaxHealth
if healthRatio < 0.3 then
rawUrgency += 40
elseif healthRatio < 0.6 then
rawUrgency += 20
end
end
end
-- Nearby players fleeing
local fleeCount = 0
for _, p in ipairs(Players:GetPlayers()) do
if p ~= LocalPlayer and p.Character then
local h = p.Character:FindFirstChildOfClass("Humanoid")
if h and h.WalkSpeed > 20 then fleeCount += 1 end
end
end
rawUrgency += math.min(30, fleeCount * 10)
-- Persona modifier
if AIState.persona == "Aggressive" then
rawUrgency = rawUrgency * 1.3
elseif AIState.persona == "Passive" then
rawUrgency = rawUrgency * 0.6
end
rawUrgency = math.min(100, rawUrgency)
-- Smooth with rolling average
table.insert(UrgencySystem.history, rawUrgency)
if #UrgencySystem.history > UrgencySystem.MAX_HIST then
table.remove(UrgencySystem.history, 1)
end
local sum = 0
for _, v in ipairs(UrgencySystem.history) do sum += v end
UrgencySystem.level = sum / #UrgencySystem.history
end
function UrgencySystem.getLabel()
if UrgencySystem.level >= 75 then return "PANIC", COLORS.danger
elseif UrgencySystem.level >= 50 then return "HIGH", COLORS.warning
elseif UrgencySystem.level >= 25 then return "MED", COLORS.accent
else return "CALM", COLORS.success
end
end
-- Wire urgency into replay speed — higher urgency = faster action cadence
local function getReplayInterval()
local base = 0.2
local urgencyFactor = 1 - (UrgencySystem.level / 100) * 0.7
return math.max(0.05, base * urgencyFactor)
end
-- Update urgency every second in heartbeat
local lastUrgencyTick = 0
RunService.Heartbeat:Connect(function(dt)
lastUrgencyTick += dt
if lastUrgencyTick >= 1 then
lastUrgencyTick = 0
UrgencySystem.update()
end
end)
-- ============================================================
-- SECTION 37: GOAL INFERENCE ENGINE
-- ============================================================
--[[
Tries to infer what the player's current goal is
based on observed actions + mental map context.
Goals feed into the prediction engine and LLM prompts.
Mimics goal-directed planning in modern AI agents.
]]
local GoalEngine = {}
GoalEngine.currentGoal = "explore" -- default
GoalEngine.goalHistory = {}
GoalEngine.goalScores = {}
local GOAL_SIGNALS = {
explore = {"move", "move", "move", "move"},
combat = {"sprint_start", "jump", "move", "sprint_start"},
interact_ui = {"ui_click", "ui_click", "move"},
collect = {"move", "ui_click", "move", "ui_click"},
escape = {"sprint_start", "move", "jump", "sprint_start", "move"},
idle = {"wait", "wait", "wait"},
}
function GoalEngine.infer()
if #EAT.workingMemory < 4 then return GoalEngine.currentGoal end
-- Build recent action string
local recent = {}
local start = math.max(1, #EAT.workingMemory - 5)
for i = start, #EAT.workingMemory do
table.insert(recent, EAT.workingMemory[i].type or "?")
end
GoalEngine.goalScores = {}
for goal, signals in pairs(GOAL_SIGNALS) do
local score = 0
for i, sig in ipairs(signals) do
if recent[i] == sig then score += 1 end
end
GoalEngine.goalScores[goal] = score / #signals
end
-- Threat override
if AIState.threatDetected then
GoalEngine.goalScores["escape"] = 1.0
end
-- Pick highest
local bestGoal = GoalEngine.currentGoal
local bestScore = 0
for goal, score in pairs(GoalEngine.goalScores) do
if score > bestScore then
bestScore = score
bestGoal = goal
end
end
if bestGoal ~= GoalEngine.currentGoal then
table.insert(GoalEngine.goalHistory, {
from = GoalEngine.currentGoal,
to = bestGoal,
t = os.time(),
})
GoalEngine.currentGoal = bestGoal
print("[VoidAI:Goal] Inferred goal: " .. bestGoal)
end
return GoalEngine.currentGoal
end
-- Patch LLM prompt to include goal
local _origBuildPrompt = LLMController.buildPrompt
function LLMController.buildPrompt(gameState)
local base = _origBuildPrompt(gameState)
if not base then return nil end
local urgLabel = UrgencySystem.getLabel()
local goal = GoalEngine.infer()
local anomalies= AnomalyDetector.getUnpromoted()
local anomStr = ""
for i, a in ipairs(anomalies) do
if i > 3 then break end
anomStr = anomStr .. a.event .. "(x" .. a.count .. ") "
end
return base
.. string.format("\nInferred goal: %s", goal)
.. string.format("\nUrgency level: %s (%.0f/100)", urgLabel, UrgencySystem.level)
.. string.format("\nUnexplained events: %s", anomStr ~= "" and anomStr or "none")
.. string.format("\nActive macros: %d | Behavior clusters: %d",
(function() local c=0; for _ in pairs(EAT.macros) do c+=1 end; return c end)(),
#EAT.clusters)
end
-- ============================================================
-- SECTION 38: SELF-ASSESSMENT REPORT
-- ============================================================
--[[
Periodically generates a plain-text self-assessment of the AI's
current knowledge state. Useful for the user to understand what
the AI knows and what it still needs to learn.
Also used as context when switching from Local AI to LLM mode.
]]
local SelfAssessment = {}
function SelfAssessment.generate()
local place = getCurrentPlace()
local urgLabel = UrgencySystem.getLabel()
local goal = GoalEngine.currentGoal
local macroCount, patternCount = 0, 0
for _ in pairs(EAT.macros) do macroCount += 1 end
for _ in pairs(EAT.reinforcedPatterns) do patternCount += 1 end
local anomalies = AnomalyDetector.getUnpromoted()
local weakAreas = {}
local strongAreas= {}
if place then
if place.confidence.movement < 40 then table.insert(weakAreas, "movement")
else table.insert(strongAreas, "movement") end
if place.confidence.ui < 40 then table.insert(weakAreas, "UI interaction")
else table.insert(strongAreas, "UI interaction") end
if place.confidence.sequences < 40 then table.insert(weakAreas, "action sequences")
else table.insert(strongAreas, "action sequences") end
end
local lines = {
"=== VoidAI Self-Assessment ===",
"Game: " .. (place and place.placeName or "Unknown"),
"Sessions: " .. (place and #place.sessions or 0),
"Goal: " .. goal,
"Urgency: " .. urgLabel .. string.format(" (%.0f/100)", UrgencySystem.level),
"",
"KNOWLEDGE:",
" Strong: " .. (#strongAreas > 0 and table.concat(strongAreas, ", ") or "none yet"),
" Weak: " .. (#weakAreas > 0 and table.concat(weakAreas, ", ") or "none"),
"",
"LEARNED STRUCTURES:",
" Macros: " .. macroCount,
" Clusters: " .. #EAT.clusters,
" Patterns: " .. patternCount,
" Anomalies:" .. #anomalies .. " unresolved",
"",
"RECOMMENDATIONS:",
}
if #weakAreas > 0 then
table.insert(lines, " → Train more: " .. table.concat(weakAreas, ", "))
end
if macroCount < 3 then
table.insert(lines, " → Play longer to build macro library")
end
if #EAT.clusters < 10 then
table.insert(lines, " → Explore more areas to build mental map")
end
if place and #place.sessions >= 5 and macroCount >= 3 then
table.insert(lines, " ✓ AI is ready for LLM-assisted play")
end
table.insert(lines, "")
table.insert(lines, "Generated: " .. tostring(os.time()))
local report = table.concat(lines, "\n")
-- Save to file
local path = BRAIN_FOLDER .. "/assessment_" .. getPlaceId() .. ".txt"
Exec.writeFile(path, report)
return report
end
-- Add to chat commands
CHAT_COMMANDS["assess"] = function()
local report = SelfAssessment.generate()
-- Show first 3 lines as notification
local firstLines = report:split("\n")
local preview = ""
for i = 1, math.min(3, #firstLines) do
preview = preview .. firstLines[i] .. " | "
end
VoidGUI.notify(preview)
print(report)
end
-- ============================================================
-- SECTION 39: REPLAY SPEED CONTROL + LOOP MODES
-- ============================================================
--[[
Gives the user fine control over how the AI replays sessions.
Modes:
- Normal: plays actions at recorded pace
- Fast: 2x speed
- Cautious: 0.5x speed, more pauses
- Loop: repeats best session forever
- Once: plays once then stops
Also integrates urgency — high urgency overrides to Fast mode.
]]
local ReplayControl = {}
ReplayControl.speed = 1.0 -- multiplier
ReplayControl.loopMode = "loop" -- "loop" | "once"
ReplayControl.paused = false
local SPEED_PRESETS = {
Cautious = 0.5,
Normal = 1.0,
Fast = 2.0,
Panic = 4.0, -- auto-set by urgency
}
function ReplayControl.setSpeed(preset)
ReplayControl.speed = SPEED_PRESETS[preset] or 1.0
VoidGUI.notify("Replay speed: " .. preset)
end
function ReplayControl.getInterval()
local base = 0.2
local urgency = UrgencySystem.level
-- High urgency overrides to faster replay
local urgencyMod = 1 - (urgency / 100) * 0.7
local finalInterval = (base / ReplayControl.speed) * urgencyMod
return math.max(0.04, finalInterval)
end
-- Patch main heartbeat ghost/lend step to use ReplayControl interval
local replayAccum = 0
RunService.Heartbeat:Connect(function(dt)
if AIState.mode ~= "ghost" and AIState.mode ~= "lend" then
replayAccum = 0
return
end
if AIState.isPaused then return end
replayAccum += dt
if replayAccum >= ReplayControl.getInterval() then
replayAccum = 0
GhostController.stepReplay()
end
end)
-- ============================================================
-- SECTION 40: MULTI-BRAIN SUPPORT
-- ============================================================
--[[
Allows saving and loading multiple named brain profiles
for the same game. Useful if you want a "grind" brain
and a "exploration" brain for the same experience.
Named brains are stored as universe_ID_profilename.json
]]
local MultiBrain = {}
MultiBrain.profiles = {}
function MultiBrain.listProfiles()
local files = Exec.listFiles(BRAIN_FOLDER)
local profiles = {}
local prefix = "universe_" .. getUniverseId()
for _, f in ipairs(files) do
if f:find(prefix) then
-- Extract profile name from filename
local name = f:match("universe_%d+_?(.-)%.json") or "default"
table.insert(profiles, {file = f, name = name == "" and "default" or name})
end
end
return profiles
end
function MultiBrain.saveAs(profileName)
local safeName = profileName:gsub("[^%w%-_]", "")
if safeName == "" then safeName = "default" end
local path = BRAIN_FOLDER .. "/universe_" .. getUniverseId() .. "_" .. safeName .. ".json"
local ok, encoded = pcall(HttpService.JSONEncode, HttpService, Brain)
if ok then
Exec.writeFile(path, encoded)
VoidGUI.notify("Brain saved as: " .. safeName)
end
end
function MultiBrain.loadProfile(profileName)
local safeName = profileName:gsub("[^%w%-_]", "")
local path = BRAIN_FOLDER .. "/universe_" .. getUniverseId() .. "_" .. safeName .. ".json"
if not Exec.isFile(path) then
VoidGUI.notify("Profile not found: " .. safeName)
return false
end
local raw = Exec.readFile(path)
if not raw then return false end
local ok, decoded = pcall(HttpService.JSONDecode, HttpService, raw)
if ok and decoded then
Brain = decoded
Brain.baseKnowledge = BASE_KNOWLEDGE
EAT.loadMacros()
EAT.loadClusters()
VoidGUI.notify("Loaded brain: " .. safeName)
VoidGUI.updateDisplay()
return true
end
return false
end
-- Chat commands for multi-brain
CHAT_COMMANDS["saveas"] = function()
-- /void saveas profilename
-- Parse not possible from single command, use notify to guide
VoidGUI.notify("Type /void saveas_<name> to save a profile")
end
-- Dynamic command for /void saveas_grind, /void saveas_explore etc.
local _origChatHandler = nil
pcall(function()
LocalPlayer.Chatted:Connect(function(msg)
local lower = msg:lower()
local parts = lower:split(" ")
if parts[1] ~= CHAT_PREFIX then return end
local cmd = parts[2] or ""
-- Handle saveas_ prefix
if cmd:sub(1, 7) == "saveas_" then
local profileName = cmd:sub(8)
if profileName and profileName ~= "" then
MultiBrain.saveAs(profileName)
end
elseif cmd:sub(1, 5) == "load_" then
local profileName = cmd:sub(6)
if profileName and profileName ~= "" then
MultiBrain.loadProfile(profileName)
end
end
end)
end)
-- ============================================================
-- SECTION 41: PERFORMANCE PROFILER
-- ============================================================
--[[
Tracks how long each VoidAI subsystem takes per frame.
Displayed in stats tab. Helps identify bottlenecks on low-end devices.
Auto-disables expensive subsystems if they exceed budget.
]]
local Profiler = {}
Profiler.budgetMs = 2.0 -- max ms per frame allowed for VoidAI
Profiler.times = {
observer = 0,
eat = 0,
atm = 0,
threat = 0,
urgency = 0,
total = 0,
}
Profiler.warnings = {}
Profiler.enabled = true
local function profiledRun(label, fn)
if not Profiler.enabled then fn(); return end
local start = os.clock()
local ok, err = pcall(fn)
local elapsed = (os.clock() - start) * 1000 -- ms
Profiler.times[label] = Profiler.times[label] * 0.8 + elapsed * 0.2 -- EMA
if elapsed > Profiler.budgetMs then
table.insert(Profiler.warnings, {
label = label,
ms = elapsed,
time = os.time(),
})
-- Cap warnings list
if #Profiler.warnings > 20 then
table.remove(Profiler.warnings, 1)
end
end
if not ok then
warn("[VoidAI:Profiler] Error in " .. label .. ": " .. tostring(err))
end
end
-- ============================================================
-- SECTION 42: FINAL GUI POLISH — URGENCY + GOAL + PREDICTION
-- ============================================================
task.spawn(function()
task.wait(1.5)
if not screenGui then return end
local panel = screenGui:FindFirstChild("VoidAIPanel")
if not panel then return end
local scrollFrames = {}
for _, child in ipairs(panel:GetChildren()) do
if child:IsA("ScrollingFrame") then
table.insert(scrollFrames, child)
end
end
local mainScroll = scrollFrames[1]
local statsScroll = scrollFrames[2]
-- Add urgency + goal bar to main tab
if mainScroll then
local urgencyLabel = makeLabel(mainScroll,
"Urgency: CALM | Goal: explore",
UDim2.new(1,-12,0,14),
UDim2.new(0,6,0,422),
9, COLORS.success)
task.spawn(function()
while true do
task.wait(1)
if not urgencyLabel or not urgencyLabel.Parent then break end
local label, color = UrgencySystem.getLabel()
local goal = GoalEngine.currentGoal
urgencyLabel.Text = string.format(
"Urgency: %s (%.0f) | Goal: %s",
label, UrgencySystem.level, goal)
urgencyLabel.TextColor3 = color
end
end)
mainScroll.CanvasSize = UDim2.new(0,0,0,700)
end
-- Add profiler readout to stats tab
if statsScroll then
makeLabel(statsScroll,
"⚡ Subsystem Timing (ms avg)",
UDim2.new(1,-12,0,14),
UDim2.new(0,6,0,440),
11, COLORS.accent).Font = Enum.Font.GothamBold
local profLabels = {}
local subsystems = {"observer","eat","atm","threat","urgency","total"}
for i, sys in ipairs(subsystems) do
local lbl = makeLabel(statsScroll,
sys .. ": 0.0ms",
UDim2.new(1,-12,0,13),
UDim2.new(0,6,0,456 + (i-1)*14),
9, COLORS.textDim)
profLabels[sys] = lbl
end
task.spawn(function()
while true do
task.wait(1)
for sys, lbl in pairs(profLabels) do
if lbl and lbl.Parent then
local ms = Profiler.times[sys] or 0
local color = ms > 1.5 and COLORS.danger
or ms > 0.8 and COLORS.warning
or COLORS.success
lbl.Text = string.format("%s: %.2fms", sys, ms)
lbl.TextColor3 = color
end
end
end
end)
statsScroll.CanvasSize = UDim2.new(0,0,0,580)
end
-- Add self-assessment button to stats tab
if statsScroll then
local assessBtn = makeButton(statsScroll,
"📋 Generate Self-Assessment",
UDim2.new(1,-12,0,28),
UDim2.new(0,6,0,548),
COLORS.panel)
assessBtn.MouseButton1Click:Connect(function()
local report = SelfAssessment.generate()
local lines = report:split("\n")
VoidGUI.notify(lines[1] .. " — see console")
print(report)
end)
statsScroll.CanvasSize = UDim2.new(0,0,0,620)
end
-- Replay speed buttons in settings tab
local settingsScroll = scrollFrames[4]
if settingsScroll then
makeLabel(settingsScroll,
"Replay Speed",
UDim2.new(1,-12,0,14),
UDim2.new(0,6,0,640),
11, COLORS.textDim)
local speedPresets = {"Cautious","Normal","Fast"}
for i, preset in ipairs(speedPresets) do
local spBtn = makeButton(settingsScroll,
preset,
UDim2.new(1/3,-5,0,24),
UDim2.new((i-1)/3, 3, 0, 658),
ReplayControl.speed == SPEED_PRESETS[preset] and COLORS.accent or COLORS.panel)
spBtn.TextSize = 10
spBtn.MouseButton1Click:Connect(function()
ReplayControl.setSpeed(preset)
end)
end
-- Loop mode toggle
local loopBtn = makeButton(settingsScroll,
"Loop: " .. ReplayControl.loopMode,
UDim2.new(1,-12,0,26),
UDim2.new(0,6,0,688),
COLORS.panel)
loopBtn.TextSize = 10
loopBtn.MouseButton1Click:Connect(function()
ReplayControl.loopMode = ReplayControl.loopMode == "loop" and "once" or "loop"
loopBtn.Text = "Loop: " .. ReplayControl.loopMode
end)
settingsScroll.CanvasSize = UDim2.new(0,0,0,740)
end
end)
-- ============================================================
-- SECTION 43: LOOP MODE ENFORCEMENT
-- ============================================================
-- Patch GhostController to respect loopMode = "once"
local _baseStepForLoop = GhostController.stepReplay
local hasPlayedOnce = false
function GhostController.stepReplayFinal()
if ReplayControl.loopMode == "once" then
if not GhostController.replaySession then return end
local actions = GhostController.replaySession.actions
if GhostController.replayIndex > #(actions or {}) then
if not hasPlayedOnce then
hasPlayedOnce = true
GhostController.reclaim()
VoidGUI.notify("Replay complete (once mode)")
end
return
end
end
hasPlayedOnce = false
_baseStepForLoop()
end
GhostController.stepReplay = GhostController.stepReplayFinal
-- ============================================================
-- SECTION 44: COMPLETE CHAT COMMAND REFERENCE
-- ============================================================
-- Add remaining missing commands and a full help output
CHAT_COMMANDS["help"] = function()
local cmds = {
"--- VoidAI Commands (/void <cmd>) ---",
"learn / stop — toggle observer mode",
"ghost / lend — toggle ghost / lend mode",
"llm — toggle LLM mode",
"eat — toggle Extended Adaptive Thinking",
"atm — toggle Adaptive Thinking (AFK mode)",
"afk — force AFK trigger",
"reclaim — take back control",
"pause — pause AI",
"safe/danger/exit/item — tag current zone",
"save — save brain now",
"assess — generate self-assessment",
"predict — show next action prediction",
"forget — apply forgetting curve",
"macros — list learned macros",
"status — show current AI status",
"stealth — toggle stealth mode",
"saveas_<name> — save brain profile",
"load_<name> — load brain profile",
"help — show this list",
}
for _, line in ipairs(cmds) do print("[VoidAI] " .. line) end
VoidGUI.notify("Full command list printed to console")
end
CHAT_COMMANDS["speed"] = function()
local speeds = {"Cautious (0.5x)","Normal (1x)","Fast (2x)"}
VoidGUI.notify("Speeds: " .. table.concat(speeds, " | ") .. " — use /void speed_<name>")
end
-- Dynamic speed command
pcall(function()
LocalPlayer.Chatted:Connect(function(msg)
local lower = msg:lower()
local parts = lower:split(" ")
if parts[1] ~= CHAT_PREFIX then return end
local cmd = parts[2] or ""
if cmd:sub(1,6) == "speed_" then
local preset = cmd:sub(7)
local map = {cautious="Cautious", normal="Normal", fast="Fast"}
if map[preset] then
ReplayControl.setSpeed(map[preset])
end
end
end)
end)
-- ============================================================
-- SECTION 45: STARTUP SUMMARY
-- ============================================================
local function printStartupSummary()
local place = getCurrentPlace()
local mc, patc = 0, 0
for _ in pairs(EAT.macros) do mc += 1 end
for _ in pairs(EAT.reinforcedPatterns) do patc += 1 end
local function pad(s, n)
s = tostring(s):sub(1, n)
while #s < n do s = s .. " " end
return s
end
print("╔════════════════════════════════════╗")
print("║ VoidAI v1.0 — Ready ║")
print("╠════════════════════════════════════╣")
print("║ Platform : " .. pad(isMobile and "Mobile" or "PC", 25) .. "║")
print("║ Universe : " .. pad(getUniverseId(), 25) .. "║")
print("║ Place : " .. pad(getPlaceId(), 25) .. "║")
if place then
print("║ Game : " .. pad((place.placeName or "Unknown"), 25) .. "║")
print("║ Sessions : " .. pad(#place.sessions, 25) .. "║")
print("║ Conf Mov : " .. pad(place.confidence.movement .. "%", 25) .. "║")
print("║ Conf UI : " .. pad(place.confidence.ui .. "%", 25) .. "║")
print("║ Conf Seq : " .. pad(place.confidence.sequences .. "%", 25) .. "║")
end
print("║ Macros : " .. pad(mc, 25) .. "║")
print("║ Clusters : " .. pad(#EAT.clusters, 25) .. "║")
print("║ Patterns : " .. pad(patc, 25) .. "║")
print("╠════════════════════════════════════╣")
if isPC then
print("║ Keybinds: G=Learn H=Lend ║")
print("║ J=GUI F=Reclaim ║")
else
print("║ Mobile: Tap floating buttons ║")
print("║ ✋ = Reclaim control ║")
end
print("╠════════════════════════════════════╣")
print("║ Chat commands: /void help ║")
print("╚════════════════════════════════════╝")
end
-- ============================================================
-- SECTION 46: CONSOLIDATED FINAL HEARTBEAT
-- ============================================================
-- Single authoritative heartbeat — replaces fragmented ones above.
-- All per-frame logic is profiled and rate-limited here.
local _fb_learn = 0
local _fb_threat = 0
local _fb_urgency = 0
local _fb_save = 0
local _fb_goal = 0
local _fb_afk = 0
local _fb_eat = 0
local _fb_atm = 0
RunService.Heartbeat:Connect(function(dt)
updateFPS(dt)
AIState.tickCounter += 1
updateSchedule(dt)
-- AFK check every 5s
_fb_afk += dt
if _fb_afk >= 5 then
_fb_afk = 0
if not AdaptiveThinking.manualOverride then
local idle = os.time() - lastInputTime
if idle >= AdaptiveThinking.afkThreshold
and not AdaptiveThinking.active
and (AIState.mode == "idle" or AIState.mode == "observing") then
AdaptiveThinking.start("AFK auto (" .. idle .. "s)")
end
end
end
-- Threat detection every 0.5s (always runs regardless of FPS)
_fb_threat += dt
if _fb_threat >= 0.5 then
_fb_threat = 0
local ok2 = pcall(function()
local isThreat, tName = ThreatDetector.scan()
if isThreat and not AIState.threatDetected then
ThreatDetector.respondToThreat(tName)
end
if AIState.threatCooldown > 0 then
AIState.threatCooldown -= 0.5
if AIState.threatCooldown <= 0 then
AIState.threatDetected = false
end
end
end)
if not ok2 then end -- silent fail on threat scan errors
end
-- Observer learning (adaptive cooldown)
_fb_learn += dt
if _fb_learn >= LearnCooldown then
_fb_learn = 0
if AIState.isLearning and AIState.currentSession then
pcall(function()
local char = LocalPlayer.Character
if not char then return end
local hrp = char:FindFirstChild("HumanoidRootPart")
if not hrp then return end
Observer.recordMovement(hrp.Position)
-- Feed EAT if active
if EAT.active then
EAT.pushChunk("move")
EAT.reinforcePattern(EAT.getWorkingMemoryKey(), "self")
EAT.addToCluster(hrp.Position, "visited")
end
-- Anomaly check
if #AIState.currentSession.actions > 0 then
local last = AIState.currentSession.actions[#AIState.currentSession.actions]
AnomalyDetector.check(last.type or "unknown", GoalEngine.currentGoal)
end
end)
end
end
-- EAT sampling (adaptive per FPS + player count)
if EAT.active then
_fb_eat += dt
local pcount = math.max(1, #Players:GetPlayers() - 1)
local eatRate = (FPS > 50 and pcount <= 5) and 0.2
or (FPS > 30) and 0.35
or 0.6
if _fb_eat >= eatRate then
_fb_eat = 0
pcall(EAT.observeSelf)
pcall(EAT.observeOthers)
end
end
-- ATM sampling
if AdaptiveThinking.active then
_fb_atm += dt
if _fb_atm >= AdaptiveThinking.sampleRate then
_fb_atm = 0
pcall(AdaptiveThinking.samplePlayers)
end
end
-- Urgency update every 1s
_fb_urgency += dt
if _fb_urgency >= 1 then
_fb_urgency = 0
pcall(UrgencySystem.update)
end
-- Goal inference + prediction every 3s
_fb_goal += dt
if _fb_goal >= 3 then
_fb_goal = 0
pcall(GoalEngine.infer)
if EAT.active then
pcall(EAT.predictNextAction)
end
end
-- LLM step
if AIState.mode == "llm" and not AIState.isPaused then
AIState.llmCooldown = (AIState.llmCooldown or 0) + dt
if AIState.llmCooldown >= LLMController.callCooldown then
AIState.llmCooldown = 0
pcall(LLMController.callLLM)
end
end
-- Batch save + forgetting curve + sequence analysis every 30s
_fb_save += dt
if _fb_save >= 30 then
_fb_save = 0
if AIState.isLearning or EAT.active or AdaptiveThinking.active then
pcall(EAT.saveMacros)
pcall(EAT.saveClusters)
pcall(saveBrain)
end
-- Every 5 minutes: forgetting curve + sequence analysis
if AIState.tickCounter % 10 == 0 then
pcall(EAT.applyForgettingCurve)
pcall(SequenceDetector.analyze)
end
end
end)
-- ============================================================
-- SECTION 47: BRAIN VERSION MIGRATION
-- ============================================================
local CURRENT_VERSION = "1.0"
local function migrateBrain()
if not Brain then return end
if Brain.version == CURRENT_VERSION then return end
Brain.version = CURRENT_VERSION
for _, place in pairs(Brain.places or {}) do
if not place.macros then place.macros = {} end
if not place.clusters then place.clusters = {} end
if not place.topUIButtons then place.topUIButtons = {} end
if not place.totalActions then place.totalActions = 0 end
if not place.totalPlaytime then place.totalPlaytime = 0 end
if not place.confidence then
place.confidence = { movement=0, ui=0, sequences=0 }
end
if not place.mentalMap then
place.mentalMap = {
landmarks={}, safeZones={}, dangerZones={},
itemSpawns={}, exits={},
}
end
for _, session in ipairs(place.sessions or {}) do
if not session.type then session.type = "self" end
if not session.priority then session.priority = 1 end
if not session.starred then session.starred = false end
if not session.startTime then session.startTime = session.id end
if not session.movements then session.movements = {} end
if not session.uiClicks then session.uiClicks = {} end
if not session.sequences then session.sequences = {} end
if not session.actions then session.actions = {} end
end
end
saveBrain()
print("[VoidAI] Brain migrated to v" .. CURRENT_VERSION)
end
-- ============================================================
-- SECTION 48: MOBILE FINAL WIRING
-- ============================================================
if isMobile then
task.spawn(function()
task.wait(3)
local function hookThumbstick()
local pgui = LocalPlayer:FindFirstChild("PlayerGui")
if not pgui then return end
for _, gui in ipairs(pgui:GetChildren()) do
local ts = gui:FindFirstChild("DynamicThumbstick", true)
or gui:FindFirstChild("Thumbstick", true)
if ts then
RunService.Heartbeat:Connect(function()
if not AIState.isLearning then return end
if AIState.tickCounter % 3 ~= 0 then return end
local char = LocalPlayer.Character
if not char then return end
local hrp = char:FindFirstChild("HumanoidRootPart")
if hrp then Observer.recordMovement(hrp.Position) end
end)
return true
end
end
return false
end
if not hookThumbstick() then
PlayerGui.ChildAdded:Connect(function()
task.wait(1)
hookThumbstick()
end)
end
end)
end
-- ============================================================
-- ENTRY POINT
-- ============================================================
local ok, err = pcall(function()
-- Core setup
Exec.makeFolder(BRAIN_FOLDER)
loadConfig()
-- Apply config to live state
if Config.stealthMode then AIState.isStealth = true end
if Config.activePersona then AIState.persona = Config.activePersona end
if Config.keybinds then
for action, keyName in pairs(Config.keybinds) do
if KEY_MAP[keyName] then
Keybinds.binds[action] = KEY_MAP[keyName]
end
end
end
-- Load + migrate brain
loadBrain()
migrateBrain()
-- Build GUI
VoidGUI.build()
if AIState.isStealth then
if mainFrame then mainFrame.Visible = false end
if miniButton then miniButton.Visible = true end
end
-- Deferred post-init (let game fully load first)
task.spawn(function()
task.wait(2)
-- Load EAT persistent data
EAT.loadMacros()
EAT.loadClusters()
-- Startup analysis passes
EAT.applyForgettingCurve()
SequenceDetector.analyze()
GoalEngine.infer()
-- Build threat HUD
buildThreatHUD()
-- Horror game detection + safe object pre-tagging
detectAndConfigureHorror()
-- Live display update loop
task.spawn(function()
while true do
task.wait(1)
VoidGUI.updateDisplay()
end
end)
-- Welcome notification
if not AIState.isStealth then
local place = getCurrentPlace()
local isNewBrain= (not place) or (#place.sessions == 0)
if isNewBrain then
VoidGUI.notify("VoidAI ready — press G (or tap 🧠) to start learning!")
else
local mc = 0
for _ in pairs(EAT.macros) do mc += 1 end
VoidGUI.notify(string.format(
"VoidAI ready — %d sessions | %d macros | %d clusters",
place and #place.sessions or 0, mc, #EAT.clusters))
end
end
-- Print full console summary
printStartupSummary()
end)
end)
if not ok then
warn("[VoidAI] Critical init error: " .. tostring(err))
pcall(function()
Exec.makeFolder(BRAIN_FOLDER)
loadBrain()
VoidGUI.build()
VoidGUI.notify("⚠ VoidAI: init error — check console")
print("[VoidAI] Recovery mode. Error was: " .. tostring(err))
end)
end
-- ============================================================
-- END OF VOIDAI v1.0
-- ============================================================
--[[
SYSTEM INDEX:
01 Services + platform detection
02 Executor compatibility (Synapse/KRNL/Fluxus/Arceus/Delta/Hydrogen/Codex)
03 Base knowledge (embedded WASD + mobile defaults + horror keywords)
04 Brain file system (per-universe JSON, per-place entries, cumulative)
05 Config system (persisted settings)
06 Adaptive learning cooldown + FPS monitor
07 AI state machine
08 Observer (movement, UI clicks, jump, sprint — event-driven)
09 Threat detection + survival response
10 Ghost controller (client-side AI playback)
11 Lend controller (AI takes full input control)
12 LLM controller (Claude / ChatGPT / Gemini with brain context)
13 Place transition continuity (survives teleports in same universe)
14 UI click hooks (event-driven, zero polling cost)
15 Keybind system (PC, fully rebindable via GUI)
16 Schedule mode (auto start/stop AI at set times)
17 GUI system (4 tabs, minimize to draggable floating button)
18 Threat HUD (pulsing indicator)
19 Sequence detector (cross-session pattern mining)
20 Smart pattern-aware replay
21 Jump + sprint event tracking
22 Other player passive observer
23 Confidence color coding (green/yellow/red bars)
24 Low confidence warnings before ghost/lend
25 Chat command system (/void <cmd>) — 20+ commands
26 Mental map visualizer (top-down dot map in Stats tab)
27 Brain export to readable .txt file
28 Horror game auto-detect + safe object pre-tagging
29 Schedule countdown notifications
30 Safety hooks (BindToClose, CharacterRemoving, respawn)
31 Adaptive Thinking Mode (ATM) — AFK multi-player observer
32 Extended Adaptive Thinking (EAT) — dual-source live learning
33 Pattern reinforcement (dual-source weight doubling)
34 Attention focus (per-player activity scoring)
35 Action chunking / macro system (10 built-in macro labels)
36 Forgetting curve (logarithmic time-decay confidence)
37 Next-action prediction engine (scored candidates)
38 Working memory buffer (short-term vs long-term split)
39 Behavior clustering (path generalization via centroid merge)
40 Anomaly detection + pattern promotion
41 Urgency / emotion state (0-100, CALM/MED/HIGH/PANIC)
42 Goal inference engine (explore/combat/interact/escape/etc)
43 Self-assessment report generator
44 Replay speed control (Cautious/Normal/Fast) + loop modes
45 Multi-brain profile support (saveas_/load_ commands)
46 Performance profiler (per-subsystem ms EMA tracking)
47 Consolidated heartbeat (single connection, all systems)
48 Mobile thumbstick wiring
49 Brain version migration
]]There is now a self learning ai which can observe your gameplay (and other players) and You can let it learn By spawning in a clone its a ai inside of roblox No need for api key (tho if you want you can link claude openai or gemini api keys for a actual Ai)