
Key Required

--[[
SMART RNG V5.1 - ENGINE
Dies ist LocalScript 1 von 2.
Empfohlener Ort:
StarterPlayer > StarterPlayerScripts
Dieses Script:
- liest PlayerData
- erkennt Roll-, Upgrade-, Reset- und Rune-Buttons
- bewertet wiederholbare und einmalige Upgrades gemeinsam
- berücksichtigt, dass ungenutzte RP, SM, PP und Clovers selbst Wert besitzen
- führt die beste gefundene Aktion aus
- stellt eine Bindable-Schnittstelle für das zweite GUI-LocalScript bereit
Kein Server-Hopping.
Keine Robux-Käufe.
]]
if not game:IsLoaded() then
game.Loaded:Wait()
end
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local LP = Players.LocalPlayer
local PlayerScripts = LP:WaitForChild("PlayerScripts")
local PlayerData = LP:WaitForChild("PlayerData")
--====================================================
-- BRIDGE ZUM GUI-SCRIPT
--====================================================
local function ensureChild(parent, className, name)
local existing = parent:FindFirstChild(name)
if existing and existing.ClassName ~= className then
existing:Destroy()
existing = nil
end
if not existing then
existing = Instance.new(className)
existing.Name = name
existing.Parent = parent
end
return existing
end
local Bridge = PlayerScripts:FindFirstChild("_SmartRNGBridge")
if not Bridge then
Bridge = Instance.new("Folder")
Bridge.Name = "_SmartRNGBridge"
Bridge.Parent = PlayerScripts
end
local CommandEvent =
ensureChild(Bridge, "BindableEvent", "Command")
local StateChangedEvent =
ensureChild(Bridge, "BindableEvent", "StateChanged")
local GetSnapshotFunction =
ensureChild(Bridge, "BindableFunction", "GetSnapshot")
local EngineReadyValue =
ensureChild(Bridge, "BoolValue", "EngineReady")
local EngineErrorValue =
ensureChild(Bridge, "StringValue", "EngineError")
EngineReadyValue.Value = false
EngineErrorValue.Value = ""
--====================================================
-- ENGINE-STATE
--====================================================
local E = {
Config = {
Master = false,
Strategy = "Balanced",
AutoRoll = true,
AutoRaritySpend = true,
AutoRebirth = true,
AutoRPUpgrades = true,
AutoSuperMultiplier = true,
AutoSMUpgrades = true,
AutoPrestige = true,
AutoPPUpgrades = true,
AutoAscension = true,
AutoCloverUpgrades = true,
AutoCloverRunes = false,
AutoPrestigeRunes = false,
AutoMultiplierRunes = false,
AutoPlantRunes = false,
ButtonNoclip = true,
AntiKnockback = true,
-- Der ausgewählte Button wird kurz unter den Spieler gesetzt,
-- aktiv gepulst und danach sicher neben dem Spieler geparkt.
StageButtons = true,
ReliableTouch = true,
SpendBeforeReset = true,
NormalRaritySpend = true,
},
State = {
Ready = false,
Busy = false,
RollBusy = false,
Dirty = true,
LastAction = "Engine startet",
Plan = "Warte auf Initialisierung",
Error = nil,
DecisionType = "Keine",
DecisionScore = 0,
TopDecisions = {},
ButtonCount = 0,
RepeatableCount = 0,
GhostCount = 0,
TouchMode = "Nicht geprüft",
LastTouch = {},
ActiveStagedPart = nil,
StagedButtonName = "Keiner",
TouchConfirmed = false,
PreResetName = nil,
PreResetSpends = 0,
},
Timing = {
RollInterval = 0.10,
ThinkInterval = 0.18,
ActionSettle = 0.13,
TouchCooldown = 0.10,
NotifyInterval = 0.15,
},
Limits = {
MaxPreResetSpends = 2,
RarityReserveFraction = 0.68,
NearMilestoneFraction = 0.15,
},
BankElasticity = {
RebirthPoints = 0.55,
UpgradesMultiMulti = 0.85,
PrestigePoints = 0.50,
Clovers = 0.32,
},
Stats = {},
Bools = {},
Buttons = {},
ByCategory = {},
ButtonConnections = {},
GhostParts = {},
GhostConnections = {},
ButtonStageState = {},
LastNotify = 0,
}
local OPTION_KEYS = {
"AutoRoll",
"AutoRaritySpend",
"AutoRebirth",
"AutoRPUpgrades",
"AutoSuperMultiplier",
"AutoSMUpgrades",
"AutoPrestige",
"AutoPPUpgrades",
"AutoAscension",
"AutoCloverUpgrades",
"AutoCloverRunes",
"AutoPrestigeRunes",
"AutoMultiplierRunes",
"AutoPlantRunes",
"ButtonNoclip",
"AntiKnockback",
"StageButtons",
"ReliableTouch",
}
--====================================================
-- BIG-NUMBER-NAMESPACE
--====================================================
local N = {}
N.NEG_INF = -math.huge
N.ZERO = {
Log10 = N.NEG_INF,
Number = 0,
Raw = "0",
}
N.Suffix = {
[""] = 0,
K = 3,
M = 6,
B = 9,
T = 12,
Qd = 15,
Qn = 18,
Sx = 21,
Sp = 24,
Oc = 27,
No = 30,
De = 33,
UDe = 36,
DDe = 39,
TDe = 42,
QdDe = 45,
QnDe = 48,
SxDe = 51,
SpDe = 54,
OcDe = 57,
NoDe = 60,
Vg = 63,
UVg = 66,
DVg = 69,
TVg = 72,
QdVg = 75,
QnVg = 78,
SxVg = 81,
SpVg = 84,
OcVg = 87,
NoVg = 90,
Tg = 93,
UTg = 96,
DTg = 99,
TTg = 102,
QdTg = 105,
QnTg = 108,
SxTg = 111,
SpTg = 114,
OcTg = 117,
NoTg = 120,
qg = 123,
Uqg = 126,
Dqg = 129,
Tqg = 132,
Qdqg = 135,
Qnqg = 138,
Sxqg = 141,
Spqg = 144,
Ocqg = 147,
Noqg = 150,
Qg = 153,
UQg = 156,
DQg = 159,
TQg = 162,
QdQg = 165,
QnQg = 168,
SxQg = 171,
SpQg = 174,
OcQg = 177,
NoQg = 180,
sg = 183,
Usg = 186,
Dsg = 189,
Tsg = 192,
Qdsg = 195,
Qnsg = 198,
Sxsg = 201,
Spsg = 204,
Ocsg = 207,
Nosg = 210,
Sg = 213,
USg = 216,
DSg = 219,
TSg = 222,
QdSg = 225,
QnSg = 228,
SxSg = 231,
SpSg = 234,
OcSg = 237,
NoSg = 240,
Og = 243,
UOg = 246,
DOg = 249,
TOg = 252,
QdOg = 255,
QnOg = 258,
SxOg = 261,
SpOg = 264,
OcOg = 267,
NoOg = 270,
Ng = 273,
UNg = 276,
DNg = 279,
TNg = 282,
QdNg = 285,
QnNg = 288,
SxNg = 291,
SpNg = 294,
OcNg = 297,
NoNg = 300,
Ce = 303,
UCe = 306,
DCe = 309,
TCe = 312,
QdCe = 315,
QnCe = 318,
SxCe = 321,
SpCe = 324,
OcCe = 327,
NoCe = 330,
}
function N.make(log10Value, number, raw)
return {
Log10 = log10Value,
Number = number,
Raw = raw,
}
end
function N.fromNumber(value, raw)
value = tonumber(value)
if not value or value <= 0 then
return N.ZERO
end
return N.make(
math.log10(value),
value,
raw or tostring(value)
)
end
function N.clean(text)
text = tostring(text or "")
text = text:gsub("<[^>]->", "")
text = text:gsub(",", "")
text = text:gsub("^%s+", "")
text = text:gsub("%s+$", "")
return text
end
function N.decode(value)
value = tostring(value or "0;0")
local layerText, magnitudeText =
value:match(
"^%s*([%-%d%.]+)%s*;%s*([%-%d%.eE+]+)%s*$"
)
if not layerText then
return N.fromNumber(tonumber(value), value)
end
local layer = tonumber(layerText) or 0
local magnitude = tonumber(magnitudeText) or 0
if magnitude <= 0 then
return N.ZERO
end
if layer <= 0 then
return N.fromNumber(magnitude, value)
end
if layer == 1 then
local number = nil
if magnitude <= 308 then
number = 10 ^ magnitude
end
return N.make(magnitude, number, value)
end
return N.make(
layer * 1e12 + magnitude,
nil,
value
)
end
function N.parseDisplay(text)
text = N.clean(text)
local scientific =
text:match("([%d%.]+[eE][%+%-]?%d+)")
if scientific then
return N.fromNumber(
tonumber(scientific),
scientific
)
end
local numberText, suffix =
text:match(
"([%d]+%.?[%d]*)%s*([%a]+)"
)
if not numberText then
numberText =
text:match("([%d]+%.?[%d]*)")
suffix = ""
end
local number = tonumber(numberText)
if not number then
return N.ZERO
end
suffix = suffix or ""
local exponent = N.Suffix[suffix] or 0
local log10Value =
math.log10(math.max(number, 1e-300))
+ exponent
local numeric = nil
if log10Value <= 308 then
numeric = number * 10 ^ exponent
end
return N.make(
log10Value,
numeric,
numberText .. suffix
)
end
function N.ge(a, b)
return a.Log10 >= b.Log10 - 1e-10
end
function N.gt(a, b)
return a.Log10 > b.Log10 + 1e-10
end
function N.add(a, b)
if a.Log10 == N.NEG_INF then
return b
end
if b.Log10 == N.NEG_INF then
return a
end
local high = math.max(a.Log10, b.Log10)
local low = math.min(a.Log10, b.Log10)
local resultLog =
high
+ math.log10(
1 + 10 ^ math.max(-300, low - high)
)
local number = nil
if resultLog <= 308 then
number = 10 ^ resultLog
end
return N.make(resultLog, number, "sum")
end
function N.scale(a, multiplier)
multiplier = tonumber(multiplier) or 0
if multiplier <= 0
or a.Log10 == N.NEG_INF
then
return N.ZERO
end
return N.make(
a.Log10 + math.log10(multiplier),
a.Number and a.Number * multiplier or nil,
"scaled"
)
end
function N.ratioLog(a, b)
if a.Log10 == N.NEG_INF then
return N.NEG_INF
end
if b.Log10 == N.NEG_INF then
return math.huge
end
return a.Log10 - b.Log10
end
function N.costFraction(cost, balance)
if not cost
or cost.Log10 == N.NEG_INF
or balance.Log10 == N.NEG_INF
then
return 1
end
local difference =
cost.Log10 - balance.Log10
if difference >= 0 then
return 1
end
if difference < -12 then
return 0
end
return 10 ^ difference
end
function N.format(value)
if not value
or value.Log10 == N.NEG_INF
then
return "0"
end
if value.Number and value.Number < 1000 then
if value.Number >= 100 then
return string.format("%.0f", value.Number)
elseif value.Number >= 10 then
return string.format("%.1f", value.Number)
else
return string.format("%.2f", value.Number)
end
end
local ordered = {}
for suffix, exponent in pairs(N.Suffix) do
table.insert(ordered, {
Suffix = suffix,
Exponent = exponent,
})
end
table.sort(ordered, function(a, b)
return a.Exponent < b.Exponent
end)
local selected = ordered[1]
for _, entry in ipairs(ordered) do
if entry.Exponent <= value.Log10 then
selected = entry
else
break
end
end
local displayed =
10 ^ (value.Log10 - selected.Exponent)
if displayed >= 100 then
return string.format(
"%.0f%s",
displayed,
selected.Suffix
)
elseif displayed >= 10 then
return string.format(
"%.1f%s",
displayed,
selected.Suffix
)
else
return string.format(
"%.2f%s",
displayed,
selected.Suffix
)
end
end
--====================================================
-- STATUS UND SNAPSHOT
--====================================================
function E.markDirty()
E.State.Dirty = true
end
function E.notify(force)
local now = os.clock()
if not force
and now - E.LastNotify
< E.Timing.NotifyInterval
then
return
end
E.LastNotify = now
StateChangedEvent:Fire()
end
function E.getStat(name)
return E.Stats[name] or N.ZERO
end
function E.small(name)
return E.getStat(name).Number or 0
end
function E.snapshot()
local options = {}
for _, key in ipairs(OPTION_KEYS) do
options[key] = E.Config[key]
end
local decisions = {}
for index, candidate in ipairs(
E.State.TopDecisions
) do
if index > 5 then
break
end
decisions[index] = {
Name = candidate.Name,
Type = candidate.Type,
Score = candidate.Score,
Details = candidate.Details,
}
end
return {
Ready = E.State.Ready,
Master = E.Config.Master,
Strategy = E.Config.Strategy,
Options = options,
LastAction = E.State.LastAction,
Plan = E.State.Plan,
Error = E.State.Error,
DecisionType = E.State.DecisionType,
DecisionScore = E.State.DecisionScore,
Decisions = decisions,
ButtonCount = E.State.ButtonCount,
RepeatableCount =
E.State.RepeatableCount,
GhostCount = E.State.GhostCount,
TouchMode = E.State.TouchMode,
StagedButton = E.State.StagedButtonName,
TouchConfirmed = E.State.TouchConfirmed,
Stats = {
Rarity = N.format(
E.getStat("Rarity")
),
BestRarity = N.format(
E.getStat("BestRarity")
),
Luck = N.format(
E.getStat("LuckMulti")
),
Multiplier = N.format(
E.getStat("MultiOriginal")
),
RP = N.format(
E.getStat("RebirthPoints")
),
RPM = N.format(
E.getStat("RPM")
),
SM = N.format(
E.getStat("UpgradesMultiMulti")
),
PP = N.format(
E.getStat("PrestigePoints")
),
Clovers = N.format(
E.getStat("Clovers")
),
Ascension = N.format(
E.getStat("Ascension")
),
},
}
end
GetSnapshotFunction.OnInvoke = function()
return E.snapshot()
end
--====================================================
-- PLAYERDATA-CACHE
--====================================================
local TRACKED_STATS = {
"Rarity",
"BestRarity",
"LuckMulti",
"RebirthPoints",
"RPM",
"MultiOriginal",
"Lesschancelower",
"UpgradesCashMulti",
"UpgradesMultiMulti",
"PrestigePoints",
"PPM",
"PrTime",
"PRPM",
"PSM",
"Clovers",
"CM",
"CLM",
"CRPM",
"CLSM",
"CLPP",
"Ascension",
"RuneBulk",
}
local TRACKED_BOOLS = {
"IsRollin",
"ToggleAutoRoll",
"Upgrade1Used",
"Upgrade2Used",
"Upgrade3Used",
"Upgrade4Used",
}
function E.refreshStat(name)
local object = PlayerData:FindFirstChild(name)
if not object then
E.Stats[name] = N.ZERO
return
end
if object:IsA("StringValue") then
E.Stats[name] = N.decode(object.Value)
elseif object:IsA("NumberValue")
or object:IsA("IntValue")
then
E.Stats[name] =
N.fromNumber(object.Value)
else
E.Stats[name] = N.ZERO
end
end
function E.refreshBool(name)
local object = PlayerData:FindFirstChild(name)
E.Bools[name] =
object
and object:IsA("BoolValue")
and object.Value
or false
end
function E.watchStats()
for _, name in ipairs(TRACKED_STATS) do
E.refreshStat(name)
local object =
PlayerData:FindFirstChild(name)
if object then
object.Changed:Connect(function()
E.refreshStat(name)
E.markDirty()
end)
end
end
for _, name in ipairs(TRACKED_BOOLS) do
E.refreshBool(name)
local object =
PlayerData:FindFirstChild(name)
if object then
object.Changed:Connect(function()
E.refreshBool(name)
E.markDirty()
end)
end
end
end
--====================================================
-- BUTTON-ERKENNUNG
--====================================================
local ROOT_NAMES = {
"Buttons",
"Roll System",
"asecnd",
"runez",
"runezz",
"runezzz",
"runeth",
}
function E.readString(parent, name)
local object =
parent and parent:FindFirstChild(name)
if object and object:IsA("StringValue") then
return object.Value, object
end
return nil, nil
end
function E.label(part, childName)
local child = part:FindFirstChild(childName)
return child
and child:FindFirstChildWhichIsA(
"TextLabel",
true
)
or nil
end
function E.labelText(label)
return label and N.clean(label.Text) or ""
end
function E.isTouchPart(instance)
return instance:IsA("BasePart")
and instance:FindFirstChildOfClass(
"TouchTransmitter"
) ~= nil
end
function E.categorize(meta)
if meta.Path
== "Workspace.Roll System.RollRarity"
then
return "Roll"
end
if meta.Path
== "Workspace.asecnd.RollRarity"
then
return "Ascension"
end
if meta.Path
== "Workspace.runez.RollRarity"
then
return "CloverRune"
end
if meta.Path
== "Workspace.runezz.RollRarity"
then
return "PrestigeRune"
end
if meta.Path
== "Workspace.runezzz.RollRarity"
then
return "MultiplierRune"
end
if meta.Path
== "Workspace.runeth.RollRarity"
then
return "PlantRune"
end
if meta.Name == "Rebirth Get" then
return "Rebirth"
end
if meta.Name == "Prestige Get" then
return "Prestige"
end
if meta.Name == "Super Multi Get" then
return "SuperReset"
end
if meta.CostStat == "Rarity"
and meta.GiveStat == "MultiOriginal"
then
return "RarityMultiplier"
end
if meta.CostStat == "RebirthPoints" then
return "RPUpgrade"
end
if meta.CostStat == "PrestigePoints" then
return "PPUpgrade"
end
if meta.CostStat
== "UpgradesMultiMulti"
then
return "SMUpgrade"
end
if meta.CostStat == "Clovers" then
return "CloverUpgrade"
end
return "Other"
end
function E.isBought(meta)
return string.find(
string.lower(E.labelText(meta.Up2)),
"bought",
1,
true
) ~= nil
end
function E.determineRepeatable(meta)
if E.isBought(meta) then
return false
end
local up1 =
string.lower(E.labelText(meta.Up1))
if meta.Category == "RPUpgrade" then
if meta.GiveStat == "RPM" then
return true
end
if meta.GiveStat == "LuckMulti"
and string.find(
up1,
"x luck",
1,
true
)
and not string.find(
up1,
"every rebirth",
1,
true
)
and not string.find(
up1,
"multiplier buttons",
1,
true
)
then
return true
end
end
if meta.Category == "SMUpgrade"
or meta.Category == "PPUpgrade"
or meta.Category == "CloverUpgrade"
then
return true
end
return false
end
function E.unregisterButton(part)
local meta = E.Buttons[part]
if not meta then
return
end
E.Buttons[part] = nil
local category =
E.ByCategory[meta.Category]
if category then
for index = #category, 1, -1 do
if category[index] == meta then
table.remove(category, index)
break
end
end
end
local connections =
E.ButtonConnections[part]
if connections then
for _, connection in ipairs(
connections
) do
connection:Disconnect()
end
end
E.ButtonConnections[part] = nil
E.recountButtons()
E.markDirty()
end
function E.registerButton(part)
if not E.isTouchPart(part)
or E.Buttons[part]
then
return
end
local scriptFolder =
part:FindFirstChild("Script")
local costStat, costStatObject =
E.readString(
scriptFolder,
"Whatstatcost"
)
local giveStat, giveStatObject =
E.readString(
scriptFolder,
"Whatstatgives"
)
local baseCost, baseCostObject =
E.readString(
scriptFolder,
"Cost"
)
local baseGive, baseGiveObject =
E.readString(
scriptFolder,
"Gives"
)
local required, requiredObject =
E.readString(
scriptFolder,
"Requiredrarity"
)
local formula, formulaObject =
E.readString(
scriptFolder,
"Costformula"
)
if not formula then
formula, formulaObject =
E.readString(
scriptFolder,
"Costformulas"
)
end
local meta = {
Part = part,
Path = part:GetFullName(),
Name = part.Name,
CostStat = costStat,
GiveStat = giveStat,
BaseCostRaw = baseCost,
BaseGiveRaw = baseGive,
RequiredRaw = required,
CostFormula = formula,
Up1 = E.label(part, "Up1"),
Up2 = E.label(part, "Up2"),
Up3 = E.label(part, "Up3"),
ValueObjects = {
costStatObject,
giveStatObject,
baseCostObject,
baseGiveObject,
requiredObject,
formulaObject,
},
}
meta.Category = E.categorize(meta)
meta.Repeatable =
E.determineRepeatable(meta)
E.Buttons[part] = meta
E.ByCategory[meta.Category] =
E.ByCategory[meta.Category] or {}
table.insert(
E.ByCategory[meta.Category],
meta
)
local connections = {}
local function changed()
meta.Repeatable =
E.determineRepeatable(meta)
E.recountButtons()
E.markDirty()
end
for _, label in ipairs({
meta.Up1,
meta.Up2,
meta.Up3,
}) do
if label then
table.insert(
connections,
label:GetPropertyChangedSignal(
"Text"
):Connect(changed)
)
end
end
for _, object in ipairs(
meta.ValueObjects
) do
if object then
table.insert(
connections,
object.Changed:Connect(changed)
)
end
end
table.insert(
connections,
part.AncestryChanged:Connect(
function()
if not part:IsDescendantOf(
workspace
) then
E.unregisterButton(part)
end
end
)
)
E.ButtonConnections[part] = connections
E.recountButtons()
E.markDirty()
end
function E.recountButtons()
local count = 0
local repeatable = 0
for _, meta in pairs(E.Buttons) do
count += 1
if meta.Repeatable then
repeatable += 1
end
end
E.State.ButtonCount = count
E.State.RepeatableCount = repeatable
end
function E.scanButtons()
for _, rootName in ipairs(ROOT_NAMES) do
local root =
workspace:FindFirstChild(rootName)
if root then
for _, descendant in ipairs(
root:GetDescendants()
) do
if E.isTouchPart(descendant) then
E.registerButton(descendant)
end
end
end
end
E.recountButtons()
end
function E.category(name)
return E.ByCategory[name] or {}
end
function E.single(name)
for _, meta in ipairs(E.category(name)) do
if meta.Part and meta.Part.Parent then
return meta
end
end
return nil
end
function E.currentCost(meta)
if E.isBought(meta) then
return nil
end
if meta.Category == "RPUpgrade"
or meta.Category == "PPUpgrade"
or meta.Category == "SMUpgrade"
or meta.Category == "CloverUpgrade"
then
local displayed =
N.parseDisplay(
E.labelText(meta.Up2)
)
if displayed.Log10 ~= N.NEG_INF then
return displayed
end
end
if meta.BaseCostRaw then
return N.decode(meta.BaseCostRaw)
end
return N.ZERO
end
function E.gain(meta)
if meta.Category == "Rebirth"
or meta.Category == "Prestige"
or meta.Category == "SuperReset"
or meta.Category == "Ascension"
then
return N.parseDisplay(
E.labelText(meta.Up2)
)
end
if meta.BaseGiveRaw then
if string.sub(meta.BaseGiveRaw, 1, 1)
== "*"
then
return N.fromNumber(
tonumber(
string.sub(
meta.BaseGiveRaw,
2
)
)
)
end
return N.decode(meta.BaseGiveRaw)
end
return N.ZERO
end
function E.requiredRarity(meta)
if meta.Category == "Ascension" then
return 250
end
if not meta.RequiredRaw then
return 0
end
return N.decode(meta.RequiredRaw).Number or 0
end
--====================================================
-- BUTTON-NOCLIP UND ANTI-KNOCKBACK
--====================================================
local GHOST_ROOTS = {
Buttons = true,
["Roll System"] = true,
asecnd = true,
runez = true,
runezz = true,
runezzz = true,
runeth = true,
}
function E.insideGhostRoot(instance)
local current = instance
while current and current ~= workspace do
if current.Parent == workspace
and GHOST_ROOTS[current.Name]
then
return true
end
current = current.Parent
end
return false
end
function E.registerGhostPart(part)
if E.GhostParts[part]
or not part:IsA("BasePart")
or not E.insideGhostRoot(part)
then
return
end
-- Nur Touch-Parts durchlässig lassen.
if not part:FindFirstChildOfClass(
"TouchTransmitter"
) then
return
end
E.GhostParts[part] = {
Original = part.CanCollide,
}
local connections = {}
table.insert(
connections,
part:GetPropertyChangedSignal(
"CanCollide"
):Connect(function()
if E.Config.ButtonNoclip
and part.Parent
and part.CanCollide
then
part.CanCollide = false
end
end)
)
table.insert(
connections,
part.AncestryChanged:Connect(
function()
if not part:IsDescendantOf(
workspace
) then
E.GhostParts[part] = nil
end
end
)
)
E.GhostConnections[part] =
connections
if E.Config.ButtonNoclip then
part.CanCollide = false
end
E.recountGhosts()
end
function E.recountGhosts()
local count = 0
for part in pairs(E.GhostParts) do
if part and part.Parent then
count += 1
end
end
E.State.GhostCount = count
end
function E.applyGhostSetting()
for part, data in pairs(E.GhostParts) do
if part and part.Parent then
part.CanCollide =
E.Config.ButtonNoclip
and false
or data.Original
end
end
end
function E.scanGhosts()
for rootName in pairs(GHOST_ROOTS) do
local root =
workspace:FindFirstChild(rootName)
if root then
for _, descendant in ipairs(
root:GetDescendants()
) do
E.registerGhostPart(descendant)
end
end
end
E.recountGhosts()
end
RunService.Heartbeat:Connect(function()
if not E.Config.AntiKnockback then
return
end
local character = LP.Character
local root = character
and character:FindFirstChild(
"HumanoidRootPart"
)
local humanoid = character
and character:FindFirstChildOfClass(
"Humanoid"
)
if not root
or not humanoid
or humanoid.Health <= 0
then
return
end
if humanoid.PlatformStand then
humanoid.PlatformStand = false
end
local velocity =
root.AssemblyLinearVelocity
local horizontal = Vector3.new(
velocity.X,
0,
velocity.Z
)
local maximumHorizontal =
math.max(
34,
humanoid.WalkSpeed * 2.2
)
if horizontal.Magnitude
> maximumHorizontal
then
local intended =
humanoid.MoveDirection
* humanoid.WalkSpeed
root.AssemblyLinearVelocity =
Vector3.new(
intended.X,
math.clamp(
velocity.Y,
-120,
55
),
intended.Z
)
elseif velocity.Y > 55 then
root.AssemblyLinearVelocity =
Vector3.new(
velocity.X,
55,
velocity.Z
)
end
if root.AssemblyAngularVelocity.Magnitude
> 18
then
root.AssemblyAngularVelocity =
Vector3.zero
end
end)
--====================================================
-- BUTTON-STAGING UND SICHERES PARKEN
--====================================================
function E.buttonRotation(part)
return part.CFrame - part.Position
end
function E.rememberButtonStageState(part)
local data = E.ButtonStageState[part]
if data then
return data
end
data = {
OriginalCFrame = part.CFrame,
OriginalCanCollide = part.CanCollide,
OriginalCanTouch = part.CanTouch,
}
E.ButtonStageState[part] = data
return data
end
function E.setButtonCFrame(part, targetCFrame)
if not part or not part.Parent then
return false
end
local success = pcall(function()
part.AssemblyLinearVelocity = Vector3.zero
part.AssemblyAngularVelocity = Vector3.zero
part.CanCollide = false
part.CanTouch = true
part.CFrame = targetCFrame
end)
return success
end
function E.parkButton(part)
if not part or not part.Parent then
return
end
local root = E.rootPart()
if not root then
return
end
E.rememberButtonStageState(part)
local sideDistance =
root.Size.X * 0.5
+ part.Size.X * 0.5
+ 4
local verticalOffset =
-(root.Size.Y * 0.5 + part.Size.Y * 0.5)
local parkingCFrame =
root.CFrame
* CFrame.new(
sideDistance,
verticalOffset,
0
)
* E.buttonRotation(part)
E.setButtonCFrame(part, parkingCFrame)
if E.State.ActiveStagedPart == part then
E.State.ActiveStagedPart = nil
end
E.State.StagedButtonName = "Keiner"
end
function E.parkActiveButton(exceptPart)
local active = E.State.ActiveStagedPart
if active and active ~= exceptPart then
E.parkButton(active)
end
end
function E.stageButton(part)
if not E.Config.StageButtons
or not part
or not part.Parent
then
return false
end
local root = E.rootPart()
if not root then
return false
end
E.parkActiveButton(part)
E.rememberButtonStageState(part)
-- Der Button liegt leicht in den Füßen. Dadurch besteht kurz eine
-- echte Überlappung, während firetouchinterest zusätzlich aktiv pulst.
local verticalOffset =
-(root.Size.Y * 0.5 + part.Size.Y * 0.5 - 0.35)
local activeCFrame =
root.CFrame
* CFrame.new(0, verticalOffset, 0)
* E.buttonRotation(part)
local success = E.setButtonCFrame(
part,
activeCFrame
)
if success then
E.State.ActiveStagedPart = part
E.State.StagedButtonName = part.Name
end
return success
end
function E.restoreStagedButtons()
E.parkActiveButton(nil)
for part, data in pairs(E.ButtonStageState) do
if part and part.Parent then
pcall(function()
part.CFrame = data.OriginalCFrame
part.CanCollide = data.OriginalCanCollide
part.CanTouch = data.OriginalCanTouch
end)
end
end
E.State.ActiveStagedPart = nil
E.State.StagedButtonName = "Keiner"
end
function E.touchSources()
local character = LP.Character
if not character then
return {}
end
local sources = {}
local names = {
"HumanoidRootPart",
"LowerTorso",
"UpperTorso",
"Torso",
"LeftFoot",
"RightFoot",
"Left Leg",
"Right Leg",
}
local seen = {}
for _, name in ipairs(names) do
local part = character:FindFirstChild(name)
if part
and part:IsA("BasePart")
and not seen[part]
then
seen[part] = true
table.insert(sources, part)
end
end
return sources
end
function E.buttonFingerprint(meta)
if not meta then
return tostring(E.Bools.IsRollin)
end
local values = {
E.labelText(meta.Up1),
E.labelText(meta.Up2),
E.labelText(meta.Up3),
tostring(E.Bools.IsRollin),
}
for _, statName in ipairs({
meta.CostStat,
meta.GiveStat,
"Rarity",
"RebirthPoints",
"UpgradesMultiMulti",
"PrestigePoints",
"Clovers",
}) do
if statName then
local object = PlayerData:FindFirstChild(statName)
if object and object:IsA("ValueBase") then
table.insert(values, tostring(object.Value))
end
end
end
return table.concat(values, "|")
end
function E.forceTouchPulse(source, buttonPart)
-- Zuerst ein künstliches Touch-Ende senden. Das ist wichtig, wenn der
-- Spieler oder Button schon länger still ineinander steht.
E.FireTouch(source, buttonPart, 1)
task.wait()
E.FireTouch(source, buttonPart, 0)
task.wait(0.025)
E.FireTouch(source, buttonPart, 1)
end
--====================================================
-- TOUCH-AUSFÜHRUNG
--====================================================
function E.findGlobalFunction(names)
local environments = {
_G,
}
if type(getgenv) == "function" then
local success, environment =
pcall(getgenv)
if success
and type(environment) == "table"
then
table.insert(
environments,
environment
)
end
end
if type(getfenv) == "function" then
local success, environment =
pcall(getfenv)
if success
and type(environment) == "table"
then
table.insert(
environments,
environment
)
end
end
for _, environment in ipairs(
environments
) do
for _, name in ipairs(names) do
local value =
rawget(environment, name)
if type(value) == "function" then
return value
end
end
end
return nil
end
E.FireTouch =
E.findGlobalFunction({
"firetouchinterest",
})
function E.rootPart()
local character = LP.Character
return character
and character:FindFirstChild(
"HumanoidRootPart"
)
end
function E.touch(part, meta)
if not part or not part.Parent then
return false
end
local root = E.rootPart()
if not root then
E.State.Error = "HumanoidRootPart fehlt"
return false
end
if not E.FireTouch then
E.State.TouchMode = "firetouchinterest fehlt"
E.State.Error =
"Die Ausführungsumgebung stellt "
.. "firetouchinterest nicht bereit."
EngineErrorValue.Value = E.State.Error
return false
end
E.stageButton(part)
task.wait(0.02)
local before = E.buttonFingerprint(meta)
local sources = E.touchSources()
if #sources == 0 then
E.State.Error = "Keine Character-Touchparts gefunden"
E.parkButton(part)
return false
end
local sent = false
local confirmed = false
local attempts = E.Config.ReliableTouch and 2 or 1
for attempt = 1, attempts do
local source = sources[
math.min(attempt, #sources)
]
local success, result = pcall(function()
E.forceTouchPulse(source, part)
end)
if success then
sent = true
else
E.State.Error = tostring(result)
EngineErrorValue.Value = E.State.Error
end
task.wait(0.07)
local after = E.buttonFingerprint(meta)
if after ~= before then
confirmed = true
break
end
end
-- Nie einen alten Button im Spieler liegen lassen. Er wird nach jedem
-- einzelnen Kauf oder Roll seitlich außerhalb der Hitbox geparkt.
E.parkButton(part)
E.State.TouchConfirmed = confirmed
E.State.TouchMode = confirmed
and "Puls-Touch bestätigt"
or "Puls-Touch gesendet"
return sent
end
function E.queue(meta, label)
if E.State.Busy
or not meta
or not meta.Part
or not meta.Part.Parent
then
return false
end
local now = os.clock()
local previous =
E.State.LastTouch[meta.Part] or 0
if now - previous
< E.Timing.TouchCooldown
then
return false
end
E.State.LastTouch[meta.Part] = now
E.State.Busy = true
E.State.LastAction = label
E.notify(true)
task.spawn(function()
local success = E.touch(meta.Part, meta)
if not success then
E.State.LastAction =
"Fehler: " .. label
end
task.wait(E.Timing.ActionSettle)
E.State.Busy = false
E.markDirty()
E.notify(true)
end)
return true
end
function E.rollTouch()
if E.State.RollBusy
or E.Bools.IsRollin
then
return
end
local roll = E.single("Roll")
if not roll then
return
end
E.State.RollBusy = true
task.spawn(function()
E.touch(roll.Part, roll)
task.wait(0.06)
E.State.RollBusy = false
end)
end
--====================================================
-- SMART-SCORING
--====================================================
E.Weight = {
RPUpgrade = {
RPM = 1.34,
LuckMulti = 1.31,
Lesschancelower = 1.22,
UpgradesCashMulti = 1.14,
},
SMUpgrade = {
LuckMulti = 1.34,
MultiOriginal = 1.24,
RPM = 1.22,
},
PPUpgrade = {
PrTime = 1.32,
PRPM = 1.29,
PPM = 1.23,
PSM = 1.20,
},
CloverUpgrade = {
CLM = 1.34,
CRPM = 1.30,
CM = 1.25,
CLSM = 1.17,
CLPP = 1.13,
},
}
E.CurrencyForCategory = {
RPUpgrade = "RebirthPoints",
SMUpgrade = "UpgradesMultiMulti",
PPUpgrade = "PrestigePoints",
CloverUpgrade = "Clovers",
}
function E.categoryEnabled(category)
if category == "RPUpgrade" then
return E.Config.AutoRPUpgrades
elseif category == "SMUpgrade" then
return E.Config.AutoSMUpgrades
elseif category == "PPUpgrade" then
return E.Config.AutoPPUpgrades
elseif category == "CloverUpgrade" then
return E.Config.AutoCloverUpgrades
end
return false
end
function E.baseWeight(meta)
local category = E.Weight[meta.Category]
local weight =
category
and category[meta.GiveStat]
or 0.82
if E.Config.Strategy == "FastestRarity" then
if meta.GiveStat == "LuckMulti"
or meta.GiveStat
== "Lesschancelower"
or meta.GiveStat == "CLM"
or meta.GiveStat == "CM"
then
weight += 0.27
else
weight -= 0.03
end
elseif E.Config.Strategy == "ResetRush" then
if meta.GiveStat == "RPM"
or meta.GiveStat == "PRPM"
or meta.GiveStat == "PSM"
or meta.GiveStat == "CRPM"
or meta.GiveStat
== "UpgradesCashMulti"
then
weight += 0.28
else
weight -= 0.02
end
end
return weight
end
function E.operationFactor(meta)
if meta.BaseGiveRaw
and string.sub(
meta.BaseGiveRaw,
1,
1
) == "*"
then
return math.max(
1,
tonumber(
string.sub(
meta.BaseGiveRaw,
2
)
) or 1
)
end
local gain = E.gain(meta)
local current =
E.getStat(meta.GiveStat or "")
if gain.Log10 == N.NEG_INF then
return 1.05
end
if current.Log10 == N.NEG_INF then
return 2
end
local relativeLog =
N.ratioLog(gain, current)
if relativeLog > 6 then
return 10
end
local relative =
10 ^ math.max(-12, relativeLog)
return math.max(1.01, 1 + relative)
end
function E.bankRetention(
currencyName,
cost,
balance
)
local elasticity =
E.BankElasticity[currencyName]
or 0
if elasticity <= 0 then
return 1
end
if not cost.Number or not balance.Number then
local fraction =
N.costFraction(cost, balance)
return math.max(
0.04,
(1 - fraction) ^ elasticity
)
end
if balance.Number <= 0 then
return 1
end
local remaining =
math.max(
0,
(balance.Number - cost.Number)
/ balance.Number
)
return math.max(
0.04,
remaining ^ elasticity
)
end
function E.upgradeCandidate(meta)
if E.isBought(meta)
or not E.categoryEnabled(
meta.Category
)
then
return nil
end
local currencyName =
E.CurrencyForCategory[
meta.Category
]
if not currencyName then
return nil
end
local cost = E.currentCost(meta)
if not cost
or cost.Log10 == N.NEG_INF
then
return nil
end
local balance =
E.getStat(currencyName)
if not N.ge(balance, cost) then
return nil
end
local directFactor =
E.operationFactor(meta)
local retention =
E.bankRetention(
currencyName,
cost,
balance
)
-- Das ist der zentrale Punkt:
-- Ein RP-Kauf wird nicht nur anhand des Upgrades bewertet.
-- Der Verlust der angesparten RP-Bank wird mit einberechnet,
-- weil diese selbst weitere Multiplikationen beeinflusst.
local effectiveFactor =
directFactor * retention
local score =
E.baseWeight(meta) * 100
score += math.log10(
math.max(1e-6, effectiveFactor)
) * 125
score += math.max(
0,
N.ratioLog(balance, cost)
) * 20
score -=
N.costFraction(cost, balance) * 85
if meta.Repeatable then
score += 32
else
-- Einmalige Meilenstein-Upgrades dürfen
-- nicht von den zwei RP-Repeatables verdrängt werden.
score += 230
end
if meta.Category == "RPUpgrade"
and meta.Repeatable
then
if meta.GiveStat == "RPM" then
score += 18
elseif meta.GiveStat
== "LuckMulti"
then
score += 15
end
end
local name =
E.labelText(meta.Up1)
if name == "" then
name = meta.Name
end
return {
Meta = meta,
Name = name,
Type = meta.Category,
Score = score,
Cost = cost,
Details = string.format(
"Kosten %s | direkt x%.3f | "
.. "Bank x%.3f | %s",
N.format(cost),
directFactor,
retention,
meta.Repeatable
and "wiederholbar"
or "einmalig"
),
}
end
function E.cheapestUnbought(
category,
currencyName
)
local result = nil
for _, meta in ipairs(
E.category(category)
) do
if not E.isBought(meta) then
local cost =
E.currentCost(meta)
if cost
and cost.Log10
~= N.NEG_INF
and (
not result
or cost.Log10
< result.Cost.Log10
)
then
result = {
Meta = meta,
Cost = cost,
Currency =
E.getStat(
currencyName
),
}
end
end
end
return result
end
function E.resetCandidate(
meta,
name,
currencyName,
upgradeCategory,
baseScore,
minimumFraction
)
if not meta or not meta.Part.Parent then
return nil
end
local gain = E.gain(meta)
if gain.Log10 == N.NEG_INF then
return nil
end
local current =
E.getStat(currencyName)
local after = N.add(current, gain)
local nextUpgrade =
E.cheapestUnbought(
upgradeCategory,
currencyName
)
local reachesUpgrade =
nextUpgrade
and N.ge(
after,
nextUpgrade.Cost
)
or false
local fractionLog =
N.ratioLog(gain, current)
if not reachesUpgrade
and current.Log10 ~= N.NEG_INF
and fractionLog
< math.log10(minimumFraction)
then
return nil
end
local score = baseScore
if reachesUpgrade then
score += 190
end
if fractionLog == math.huge then
score += 160
else
score += math.max(
-1,
fractionLog
) * 55
end
if E.Config.Strategy == "ResetRush" then
score += 75
elseif E.Config.Strategy
== "FastestRarity"
then
score -= 20
end
return {
Meta = meta,
Name = name,
Type = "Reset",
Score = score,
Gain = gain,
Details = string.format(
"Gewinn %s%s",
N.format(gain),
reachesUpgrade
and " | erreicht nächstes Upgrade"
or ""
),
}
end
function E.collectResetCandidates(list)
local rarity = E.small("Rarity")
if E.Config.AutoAscension
and rarity >= 250
then
local meta = E.single("Ascension")
if meta then
table.insert(list, {
Meta = meta,
Name = "Ascension",
Type = "Reset",
Score = 1120,
Gain = E.gain(meta),
Details =
"Höchste verfügbare Reset-Ebene",
})
end
end
if E.Config.AutoPrestige
and rarity >= 99
then
local candidate =
E.resetCandidate(
E.single("Prestige"),
"Prestige",
"PrestigePoints",
"PPUpgrade",
820,
0.60
)
if candidate then
table.insert(list, candidate)
end
end
if E.Config.AutoSuperMultiplier then
local meta = E.single("SuperReset")
if meta then
local required =
E.currentCost(meta)
if required
and N.ge(
E.getStat(
"MultiOriginal"
),
required
)
then
local candidate =
E.resetCandidate(
meta,
"Super Multiplier",
"UpgradesMultiMulti",
"SMUpgrade",
650,
0.45
)
if candidate then
table.insert(
list,
candidate
)
end
end
end
end
if E.Config.AutoRebirth
and rarity >= 5
then
local candidate =
E.resetCandidate(
E.single("Rebirth"),
"Rebirth",
"RebirthPoints",
"RPUpgrade",
430,
0.30
)
if candidate then
table.insert(list, candidate)
end
end
end
function E.nextRarityMilestone()
local best = E.small("BestRarity")
local nextValue = math.huge
for _, meta in ipairs(
E.category("RarityMultiplier")
) do
local required =
E.requiredRarity(meta)
if required > best
and required < nextValue
then
nextValue = required
end
end
if best < 99 then
nextValue = math.min(nextValue, 99)
end
if best < 250 then
nextValue = math.min(nextValue, 250)
end
if nextValue == math.huge then
return nil
end
return nextValue
end
function E.raritySpendCandidate(reserve)
if not E.Config.AutoRaritySpend then
return nil
end
local current = E.small("Rarity")
local best = E.small("BestRarity")
local available = current - reserve
if available <= 0 then
return nil
end
local selected = nil
for _, meta in ipairs(
E.category("RarityMultiplier")
) do
local costObject =
E.currentCost(meta)
local cost =
costObject and costObject.Number
local required =
E.requiredRarity(meta)
if cost
and cost > 0
and cost <= available
and best >= required
then
local gain = E.gain(meta)
if gain.Log10 ~= N.NEG_INF then
local efficiency =
gain.Log10
- math.log10(cost)
-- Rarity-Spend bleibt absichtlich
-- unter Reset- und guten Shop-Aktionen.
local score =
150 + efficiency * 55
local name =
E.labelText(meta.Up3)
if name == "" then
name = meta.Name
end
local candidate = {
Meta = meta,
Name = name,
Type = "RaritySpend",
Score = score,
Details = string.format(
"Rarity-Kosten %.3f | "
.. "Multiplier %s",
cost,
N.format(gain)
),
}
if not selected
or candidate.Score
> selected.Score
then
selected = candidate
end
end
end
end
return selected
end
function E.normalRarityReserve()
local current = E.small("Rarity")
local best = E.small("BestRarity")
local milestone =
E.nextRarityMilestone()
if milestone then
local distance = milestone - best
if distance > 0
and distance
<= math.max(
1,
best
* E.Limits
.NearMilestoneFraction
)
then
return current
end
end
return math.max(
5,
best
* E.Limits.RarityReserveFraction
)
end
function E.collectUpgradeCandidates(list)
for category in pairs(
E.CurrencyForCategory
) do
if E.categoryEnabled(category) then
for _, meta in ipairs(
E.category(category)
) do
local candidate =
E.upgradeCandidate(meta)
if candidate then
table.insert(
list,
candidate
)
end
end
end
end
end
function E.runeCandidate(
enabled,
category,
name,
currencyName,
cost
)
if not enabled then
return nil
end
if not N.ge(
E.getStat(currencyName),
cost
) then
return nil
end
local meta = E.single(category)
if not meta then
return nil
end
return {
Meta = meta,
Name = name,
Type = "Rune",
Score = 35,
Details =
"Rune erst nach sicheren Upgrades",
}
end
function E.collectRuneCandidates(list)
local candidates = {
E.runeCandidate(
E.Config.AutoPrestigeRunes,
"PrestigeRune",
"Prestige-Rune",
"PrestigePoints",
N.fromNumber(10)
),
E.runeCandidate(
E.Config.AutoCloverRunes,
"CloverRune",
"Clover-Rune",
"Clovers",
N.fromNumber(250)
),
E.runeCandidate(
E.Config.AutoMultiplierRunes,
"MultiplierRune",
"Multiplier-Rune",
"MultiOriginal",
N.parseDisplay("1Ce")
),
E.runeCandidate(
E.Config.AutoPlantRunes,
"PlantRune",
"Plant-Rune",
"Clovers",
N.parseDisplay("100K")
),
}
for _, candidate in ipairs(candidates) do
if candidate then
table.insert(list, candidate)
end
end
end
function E.sortCandidates(list)
table.sort(list, function(a, b)
if a.Score == b.Score then
return a.Name < b.Name
end
return a.Score > b.Score
end)
end
function E.buildCandidates()
local candidates = {}
E.collectResetCandidates(candidates)
E.collectUpgradeCandidates(candidates)
if #candidates == 0
and E.Config.NormalRaritySpend
then
local spend =
E.raritySpendCandidate(
E.normalRarityReserve()
)
if spend then
table.insert(candidates, spend)
end
end
if #candidates == 0 then
E.collectRuneCandidates(candidates)
end
E.sortCandidates(candidates)
E.State.TopDecisions = candidates
return candidates
end
function E.executeReset(candidate)
local reserve = 0
if candidate.Name == "Rebirth" then
reserve = 5
elseif candidate.Name == "Prestige" then
reserve = 99
elseif candidate.Name == "Ascension" then
reserve = 250
end
if E.Config.SpendBeforeReset
and E.Config.AutoRaritySpend
then
if E.State.PreResetName
~= candidate.Name
then
E.State.PreResetName =
candidate.Name
E.State.PreResetSpends = 0
end
if E.State.PreResetSpends
< E.Limits.MaxPreResetSpends
then
local spend =
E.raritySpendCandidate(
reserve
)
if spend then
E.State.PreResetSpends += 1
E.State.Plan =
"Vor "
.. candidate.Name
.. ": "
.. spend.Name
E.State.DecisionType =
"Rarity vor Reset"
E.State.DecisionScore =
spend.Score
return E.queue(
spend.Meta,
spend.Name
)
end
end
end
E.State.PreResetName = nil
E.State.PreResetSpends = 0
E.State.Plan =
candidate.Name
.. " durchführen | "
.. candidate.Details
E.State.DecisionType = candidate.Type
E.State.DecisionScore = candidate.Score
return E.queue(
candidate.Meta,
candidate.Name
)
end
function E.plannerStep()
if not E.Config.Master
or E.State.Busy
then
return
end
local candidates = E.buildCandidates()
local best = candidates[1]
if not best then
E.State.Plan =
"Rarity erhöhen und auf "
.. "den nächsten sinnvollen Kauf sparen"
E.State.DecisionType = "Warten"
E.State.DecisionScore = 0
E.notify()
return
end
if best.Type == "Reset" then
E.executeReset(best)
return
end
E.State.PreResetName = nil
E.State.PreResetSpends = 0
E.State.Plan =
best.Name
.. " | "
.. best.Details
E.State.DecisionType = best.Type
E.State.DecisionScore = best.Score
E.queue(best.Meta, best.Name)
end
--====================================================
-- KOMMANDOS VOM GUI
--====================================================
function E.setOption(key, value)
if E.Config[key] == nil then
return
end
E.Config[key] = value == true
if key == "ButtonNoclip" then
E.applyGhostSetting()
elseif key == "StageButtons"
and not E.Config.StageButtons
then
E.restoreStagedButtons()
end
E.markDirty()
E.notify(true)
end
function E.cycleStrategy()
local strategies = {
"Balanced",
"FastestRarity",
"ResetRush",
}
local current = 1
for index, strategy in ipairs(
strategies
) do
if strategy == E.Config.Strategy then
current = index
break
end
end
current += 1
if current > #strategies then
current = 1
end
E.Config.Strategy =
strategies[current]
E.State.LastAction =
"Strategie: "
.. E.Config.Strategy
E.markDirty()
E.notify(true)
end
function E.printDebug()
print("")
print("========================================")
print("SMART RNG V5 ENGINE DEBUG")
print("========================================")
print("Ready:", E.State.Ready)
print("Master:", E.Config.Master)
print("Strategie:", E.Config.Strategy)
print("Touch:", E.State.TouchMode)
print("Touch bestätigt:", E.State.TouchConfirmed)
print("Geparkter/Aktiver Button:", E.State.StagedButtonName)
print("Fehler:", E.State.Error or "keiner")
print("Plan:", E.State.Plan)
print("RP:", N.format(E.getStat("RebirthPoints")))
print("RPM:", N.format(E.getStat("RPM")))
print("Luck:", N.format(E.getStat("LuckMulti")))
print("SM:", N.format(E.getStat("UpgradesMultiMulti")))
print("PP:", N.format(E.getStat("PrestigePoints")))
print("")
print("Top-Entscheidungen:")
for index, candidate in ipairs(
E.State.TopDecisions
) do
if index > 8 then
break
end
print(string.format(
"%d. %s | %s | Score %.2f | %s",
index,
candidate.Name,
candidate.Type,
candidate.Score,
candidate.Details
))
end
print("========================================")
end
CommandEvent.Event:Connect(function(
command,
argument,
value
)
if command == "ToggleMaster" then
E.Config.Master =
not E.Config.Master
E.State.LastAction =
E.Config.Master
and "Smart-Automatik gestartet"
or "Smart-Automatik gestoppt"
if not E.Config.Master then
E.parkActiveButton(nil)
end
E.markDirty()
E.notify(true)
elseif command == "SetMaster" then
E.Config.Master = argument == true
if not E.Config.Master then
E.parkActiveButton(nil)
end
E.markDirty()
E.notify(true)
elseif command == "ToggleOption" then
local key = tostring(argument)
if E.Config[key] ~= nil then
E.setOption(
key,
not E.Config[key]
)
end
elseif command == "SetOption" then
E.setOption(
tostring(argument),
value
)
elseif command == "CycleStrategy" then
E.cycleStrategy()
elseif command == "Rescan" then
E.scanButtons()
E.scanGhosts()
E.State.LastAction =
"Buttons neu eingelesen"
E.markDirty()
E.notify(true)
elseif command == "PrintDebug" then
E.printDebug()
end
end)
--====================================================
-- INITIALISIERUNG
--====================================================
local function initialize()
E.watchStats()
E.scanButtons()
E.scanGhosts()
E.applyGhostSetting()
workspace.DescendantAdded:Connect(
function(instance)
if instance:IsA("BasePart")
and E.insideGhostRoot(instance)
then
task.defer(function()
E.registerGhostPart(instance)
end)
end
if E.isTouchPart(instance) then
task.defer(function()
E.registerButton(instance)
end)
end
end
)
E.State.Ready = true
E.State.LastAction =
"Engine bereit"
E.State.Plan =
"F10 oder GUI verwenden"
EngineReadyValue.Value = true
EngineErrorValue.Value = ""
E.notify(true)
end
local success, result =
xpcall(initialize, debug.traceback)
if not success then
E.State.Error = tostring(result)
E.State.LastAction =
"Initialisierungsfehler"
E.State.Plan =
"GUI öffnen und F6-Debug ausführen"
EngineErrorValue.Value =
E.State.Error
warn(
"[SMART RNG V5.1 ENGINE FEHLER]",
result
)
end
--====================================================
-- LEICHTE LOOPS
--====================================================
task.spawn(function()
while task.wait(E.Timing.RollInterval) do
if E.Config.Master
and E.Config.AutoRoll
and not E.State.Busy
then
E.rollTouch()
end
end
end)
task.spawn(function()
while task.wait(E.Timing.ThinkInterval) do
if E.Config.Master
and not E.State.Busy
then
local ok, err =
pcall(function()
E.plannerStep()
end)
if not ok then
E.State.Error = tostring(err)
E.State.LastAction =
"Planerfehler"
EngineErrorValue.Value =
E.State.Error
E.notify(true)
end
end
end
end)
print("[SMART RNG V5.1 ENGINE] Geladen.")
print("[SMART RNG V5.1 ENGINE] Warte auf GUI-LocalScript.")
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Next Script
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
--[[
SMART RNG V5.1 - GUI
Dies ist LocalScript 2 von 2.
Empfohlener Ort:
StarterPlayer > StarterPlayerScripts
Das Engine-Script und dieses GUI-Script müssen beide laufen.
Die Startreihenfolge ist egal. Das GUI wartet automatisch auf die Engine.
F10 = Master-Automatik an/aus
RightShift = GUI ein-/ausblenden
F6 = Engine-Debug in die Konsole schreiben
]]
if not game:IsLoaded() then
game.Loaded:Wait()
end
local Players = game:GetService("Players")
local UserInputService =
game:GetService("UserInputService")
local LP = Players.LocalPlayer
local PlayerGui =
LP:WaitForChild("PlayerGui")
local PlayerScripts =
LP:WaitForChild("PlayerScripts")
--====================================================
-- GUI-STATE
--====================================================
local G = {
Bridge = nil,
Command = nil,
StateChanged = nil,
GetSnapshot = nil,
ReadyValue = nil,
ErrorValue = nil,
Snapshot = nil,
Connected = false,
Gui = nil,
Main = nil,
MasterButton = nil,
StrategyButton = nil,
PlanLabel = nil,
StatusLabel = nil,
StatsLabel = nil,
DecisionLabel = nil,
EngineLabel = nil,
ToggleButtons = {},
}
local COLORS = {
Background = Color3.fromRGB(21, 22, 28),
Panel = Color3.fromRGB(31, 33, 41),
Panel2 = Color3.fromRGB(43, 46, 56),
On = Color3.fromRGB(43, 139, 82),
Off = Color3.fromRGB(132, 55, 61),
Accent = Color3.fromRGB(65, 108, 205),
Text = Color3.fromRGB(242, 242, 245),
Muted = Color3.fromRGB(166, 170, 182),
Plan = Color3.fromRGB(180, 208, 255),
Error = Color3.fromRGB(255, 120, 120),
Ready = Color3.fromRGB(115, 255, 155),
}
local TOGGLES = {
{"AutoRoll", "Auto Roll"},
{"AutoRaritySpend", "Rarity → Multiplier"},
{"AutoRebirth", "Auto Rebirth"},
{"AutoRPUpgrades", "RP-Upgrades"},
{"AutoSuperMultiplier", "Super Multiplier"},
{"AutoSMUpgrades", "SM-Upgrades"},
{"AutoPrestige", "Auto Prestige"},
{"AutoPPUpgrades", "PP-Upgrades"},
{"AutoAscension", "Auto Ascension"},
{"AutoCloverUpgrades", "Clover-Upgrades"},
{"AutoCloverRunes", "Clover-Runes"},
{"AutoPrestigeRunes", "Prestige-Runes"},
{"AutoMultiplierRunes", "Multiplier-Runes"},
{"AutoPlantRunes", "Plant-Runes"},
{"ButtonNoclip", "Button-Noclip"},
{"AntiKnockback", "Anti-Knockback"},
{"StageButtons", "Button-Teleport"},
{"ReliableTouch", "Touch-Puls"},
}
--====================================================
-- UI-HILFSFUNKTIONEN
--====================================================
local function addCorner(instance, radius)
local corner = Instance.new("UICorner")
corner.CornerRadius =
UDim.new(0, radius or 8)
corner.Parent = instance
end
local function makeLabel(
parent,
text,
position,
size,
textSize
)
local label = Instance.new("TextLabel")
label.BackgroundTransparency = 1
label.Position = position
label.Size = size
label.Font = Enum.Font.Gotham
label.Text = text
label.TextColor3 = COLORS.Text
label.TextSize = textSize or 13
label.TextWrapped = true
label.TextXAlignment =
Enum.TextXAlignment.Left
label.TextYAlignment =
Enum.TextYAlignment.Center
label.Parent = parent
return label
end
local function makeButton(
parent,
text,
position,
size
)
local button = Instance.new("TextButton")
button.Position = position
button.Size = size
button.BackgroundColor3 = COLORS.Panel2
button.BorderSizePixel = 0
button.AutoButtonColor = true
button.Font = Enum.Font.GothamBold
button.Text = text
button.TextColor3 = COLORS.Text
button.TextSize = 12
button.Parent = parent
addCorner(button, 7)
return button
end
local function makeDraggable(frame, handle)
local dragging = false
local dragStart = nil
local startPosition = nil
handle.InputBegan:Connect(function(input)
if input.UserInputType
== Enum.UserInputType.MouseButton1
or input.UserInputType
== Enum.UserInputType.Touch
then
dragging = true
dragStart = input.Position
startPosition = frame.Position
end
end)
UserInputService.InputChanged:Connect(
function(input)
if not dragging then
return
end
if input.UserInputType
== Enum.UserInputType.MouseMovement
or input.UserInputType
== Enum.UserInputType.Touch
then
local delta =
input.Position - dragStart
frame.Position = UDim2.new(
startPosition.X.Scale,
startPosition.X.Offset
+ delta.X,
startPosition.Y.Scale,
startPosition.Y.Offset
+ delta.Y
)
end
end
)
UserInputService.InputEnded:Connect(
function(input)
if input.UserInputType
== Enum.UserInputType.MouseButton1
or input.UserInputType
== Enum.UserInputType.Touch
then
dragging = false
end
end
)
end
--====================================================
-- GUI ERSTELLEN
--====================================================
local function createGui()
local old =
PlayerGui:FindFirstChild(
"_SmartRNGV5Gui"
)
if old then
old:Destroy()
end
local screen = Instance.new("ScreenGui")
screen.Name = "_SmartRNGV5Gui"
screen.ResetOnSpawn = false
screen.DisplayOrder = 1700
screen.Parent = PlayerGui
G.Gui = screen
local main = Instance.new("Frame")
main.Size = UDim2.fromOffset(570, 760)
main.Position =
UDim2.new(0, 18, 0.5, -380)
main.BackgroundColor3 =
COLORS.Background
main.BorderSizePixel = 0
main.Active = true
main.Parent = screen
G.Main = main
addCorner(main, 11)
local titleBar = Instance.new("Frame")
titleBar.Size =
UDim2.new(1, 0, 0, 44)
titleBar.BackgroundColor3 =
COLORS.Panel
titleBar.BorderSizePixel = 0
titleBar.Active = true
titleBar.Parent = main
addCorner(titleBar, 11)
local title = makeLabel(
titleBar,
"Smart RNG V5.1",
UDim2.fromOffset(14, 0),
UDim2.new(1, -110, 1, 0),
18
)
title.Font = Enum.Font.GothamBold
local hide = makeButton(
titleBar,
"—",
UDim2.new(1, -42, 0, 8),
UDim2.fromOffset(32, 28)
)
hide.MouseButton1Click:Connect(function()
screen.Enabled = false
end)
makeDraggable(main, titleBar)
G.EngineLabel = makeLabel(
main,
"Warte auf Engine-LocalScript …",
UDim2.fromOffset(14, 48),
UDim2.new(1, -28, 0, 28),
12
)
G.EngineLabel.TextColor3 = COLORS.Muted
G.MasterButton = makeButton(
main,
"SMART AUTO: AUS",
UDim2.fromOffset(12, 80),
UDim2.new(0.58, -18, 0, 42)
)
G.MasterButton.BackgroundColor3 =
COLORS.Off
G.StrategyButton = makeButton(
main,
"Strategie: Balanced",
UDim2.new(0.58, 0, 0, 80),
UDim2.new(0.42, -12, 0, 42)
)
G.StrategyButton.BackgroundColor3 =
COLORS.Accent
G.PlanLabel = makeLabel(
main,
"Plan: Warte auf Engine",
UDim2.fromOffset(14, 128),
UDim2.new(1, -28, 0, 58),
12
)
G.PlanLabel.TextColor3 = COLORS.Plan
G.StatusLabel = makeLabel(
main,
"Aktion: keine",
UDim2.fromOffset(14, 188),
UDim2.new(1, -28, 0, 70),
11
)
G.StatusLabel.TextColor3 =
COLORS.Muted
local statsPanel = Instance.new("Frame")
statsPanel.Position =
UDim2.fromOffset(12, 264)
statsPanel.Size =
UDim2.new(1, -24, 0, 126)
statsPanel.BackgroundColor3 =
COLORS.Panel
statsPanel.BorderSizePixel = 0
statsPanel.Parent = main
addCorner(statsPanel, 8)
G.StatsLabel = makeLabel(
statsPanel,
"Werte werden geladen …",
UDim2.fromOffset(12, 8),
UDim2.new(1, -24, 1, -16),
12
)
G.StatsLabel.Font = Enum.Font.Code
local decisionPanel = Instance.new("Frame")
decisionPanel.Position =
UDim2.fromOffset(12, 398)
decisionPanel.Size =
UDim2.new(1, -24, 0, 104)
decisionPanel.BackgroundColor3 =
COLORS.Panel
decisionPanel.BorderSizePixel = 0
decisionPanel.Parent = main
addCorner(decisionPanel, 8)
G.DecisionLabel = makeLabel(
decisionPanel,
"Top-Entscheidungen werden geladen …",
UDim2.fromOffset(12, 6),
UDim2.new(1, -24, 1, -12),
11
)
G.DecisionLabel.Font = Enum.Font.Code
local toolBar = Instance.new("Frame")
toolBar.Position =
UDim2.fromOffset(12, 510)
toolBar.Size =
UDim2.new(1, -24, 0, 34)
toolBar.BackgroundTransparency = 1
toolBar.Parent = main
local toolLayout = Instance.new("UIListLayout")
toolLayout.FillDirection =
Enum.FillDirection.Horizontal
toolLayout.Padding = UDim.new(0, 7)
toolLayout.Parent = toolBar
local rescanButton = makeButton(
toolBar,
"Buttons neu scannen",
UDim2.new(),
UDim2.new(0.5, -4, 1, 0)
)
local debugButton = makeButton(
toolBar,
"F6-Debug ausgeben",
UDim2.new(),
UDim2.new(0.5, -4, 1, 0)
)
local scroll =
Instance.new("ScrollingFrame")
scroll.Position =
UDim2.fromOffset(12, 552)
scroll.Size =
UDim2.new(1, -24, 1, -564)
scroll.BackgroundColor3 =
COLORS.Panel
scroll.BorderSizePixel = 0
scroll.ScrollBarThickness = 7
scroll.CanvasSize =
UDim2.fromOffset(0, 0)
scroll.Parent = main
addCorner(scroll, 8)
local padding = Instance.new("UIPadding")
padding.PaddingTop = UDim.new(0, 8)
padding.PaddingBottom = UDim.new(0, 8)
padding.PaddingLeft = UDim.new(0, 8)
padding.PaddingRight = UDim.new(0, 8)
padding.Parent = scroll
local layout = Instance.new("UIGridLayout")
layout.CellSize =
UDim2.new(0.5, -6, 0, 38)
layout.CellPadding =
UDim2.fromOffset(8, 8)
layout.SortOrder =
Enum.SortOrder.LayoutOrder
layout.Parent = scroll
for index, entry in ipairs(TOGGLES) do
local key = entry[1]
local displayName = entry[2]
local button = makeButton(
scroll,
displayName,
UDim2.new(),
UDim2.new()
)
button.LayoutOrder = index
button:SetAttribute(
"OptionKey",
key
)
button:SetAttribute(
"DisplayName",
displayName
)
G.ToggleButtons[key] = button
button.MouseButton1Click:Connect(
function()
if G.Command then
G.Command:Fire(
"ToggleOption",
key
)
end
end
)
end
layout:GetPropertyChangedSignal(
"AbsoluteContentSize"
):Connect(function()
scroll.CanvasSize =
UDim2.fromOffset(
0,
layout.AbsoluteContentSize.Y
+ 16
)
end)
G.MasterButton.MouseButton1Click:Connect(
function()
if G.Command then
G.Command:Fire(
"ToggleMaster"
)
end
end
)
G.StrategyButton.MouseButton1Click:Connect(
function()
if G.Command then
G.Command:Fire(
"CycleStrategy"
)
end
end
)
rescanButton.MouseButton1Click:Connect(
function()
if G.Command then
G.Command:Fire("Rescan")
end
end
)
debugButton.MouseButton1Click:Connect(
function()
if G.Command then
G.Command:Fire(
"PrintDebug"
)
end
end
)
end
--====================================================
-- BRIDGE VERBINDEN
--====================================================
local function connectBridge()
while not G.Connected do
local bridge =
PlayerScripts:FindFirstChild(
"_SmartRNGBridge"
)
if bridge then
local command =
bridge:FindFirstChild(
"Command"
)
local stateChanged =
bridge:FindFirstChild(
"StateChanged"
)
local getSnapshot =
bridge:FindFirstChild(
"GetSnapshot"
)
local ready =
bridge:FindFirstChild(
"EngineReady"
)
local errorValue =
bridge:FindFirstChild(
"EngineError"
)
if command
and stateChanged
and getSnapshot
and ready
and errorValue
then
G.Bridge = bridge
G.Command = command
G.StateChanged = stateChanged
G.GetSnapshot = getSnapshot
G.ReadyValue = ready
G.ErrorValue = errorValue
G.Connected = true
stateChanged.Event:Connect(
function()
-- Der normale Refresh-Loop
-- liest anschließend den Snapshot.
end
)
return
end
end
task.wait(0.25)
end
end
local function requestSnapshot()
if not G.GetSnapshot then
return nil
end
local success, snapshot = pcall(function()
return G.GetSnapshot:Invoke()
end)
if not success then
return nil
end
return snapshot
end
--====================================================
-- GUI AKTUALISIEREN
--====================================================
local function updateToggleButtons(snapshot)
local options = snapshot.Options or {}
for key, button in pairs(
G.ToggleButtons
) do
local enabled =
options[key] == true
local displayName =
button:GetAttribute(
"DisplayName"
)
button.Text =
displayName
.. (enabled and ": AN" or ": AUS")
button.BackgroundColor3 =
enabled
and COLORS.On
or COLORS.Off
end
end
local function buildDecisionText(snapshot)
local decisions = snapshot.Decisions or {}
if #decisions == 0 then
return "Keine kaufbare Aktion. "
.. "Das Script spart oder rollt weiter."
end
local lines = {}
for index, decision in ipairs(decisions) do
if index > 3 then
break
end
table.insert(
lines,
string.format(
"%d. %s | %.1f\n %s",
index,
tostring(decision.Name),
tonumber(decision.Score) or 0,
tostring(
decision.Details or ""
)
)
)
end
return table.concat(lines, "\n")
end
local function updateGui()
if not G.Gui or not G.Gui.Enabled then
return
end
if not G.Connected then
G.EngineLabel.Text =
"Warte auf Engine-LocalScript …"
G.EngineLabel.TextColor3 =
COLORS.Muted
return
end
local snapshot = requestSnapshot()
if not snapshot then
G.EngineLabel.Text =
"Engine verbunden, Snapshot fehlt"
G.EngineLabel.TextColor3 =
COLORS.Error
return
end
G.Snapshot = snapshot
if snapshot.Error
and tostring(snapshot.Error) ~= ""
then
G.EngineLabel.Text =
"Engine-Fehler: "
.. tostring(snapshot.Error)
G.EngineLabel.TextColor3 =
COLORS.Error
elseif snapshot.Ready then
G.EngineLabel.Text =
"Engine bereit | "
.. tostring(snapshot.TouchMode)
G.EngineLabel.TextColor3 =
COLORS.Ready
else
G.EngineLabel.Text =
"Engine initialisiert …"
G.EngineLabel.TextColor3 =
COLORS.Muted
end
G.MasterButton.Text =
snapshot.Master
and "SMART AUTO: AN"
or "SMART AUTO: AUS"
G.MasterButton.BackgroundColor3 =
snapshot.Master
and COLORS.On
or COLORS.Off
G.StrategyButton.Text =
"Strategie: "
.. tostring(snapshot.Strategy)
G.PlanLabel.Text =
"Plan: "
.. tostring(snapshot.Plan)
G.StatusLabel.Text = string.format(
"Aktion: %s\n"
.. "Entscheidung: %s | Score %.1f\n"
.. "Buttons %d | Repeatables %d | Ghost %d | Aktiv %s",
tostring(snapshot.LastAction),
tostring(snapshot.DecisionType),
tonumber(snapshot.DecisionScore)
or 0,
tonumber(snapshot.ButtonCount)
or 0,
tonumber(snapshot.RepeatableCount)
or 0,
tonumber(snapshot.GhostCount)
or 0,
tostring(snapshot.StagedButton or "Keiner")
)
local stats = snapshot.Stats or {}
G.StatsLabel.Text = string.format(
"Rarity: %s Best: %s\n"
.. "Luck: %s Multiplier: %s\n"
.. "RP: %s RP-Multi: %s\n"
.. "SM: %s PP: %s\n"
.. "Clovers: %s Ascension: %s",
tostring(stats.Rarity or "0"),
tostring(stats.BestRarity or "0"),
tostring(stats.Luck or "0"),
tostring(stats.Multiplier or "0"),
tostring(stats.RP or "0"),
tostring(stats.RPM or "0"),
tostring(stats.SM or "0"),
tostring(stats.PP or "0"),
tostring(stats.Clovers or "0"),
tostring(stats.Ascension or "0")
)
G.DecisionLabel.Text =
buildDecisionText(snapshot)
updateToggleButtons(snapshot)
end
--====================================================
-- START
--====================================================
createGui()
task.spawn(function()
connectBridge()
updateGui()
end)
UserInputService.InputBegan:Connect(
function(input, processed)
if processed then
return
end
if input.KeyCode == Enum.KeyCode.F10 then
if G.Command then
G.Command:Fire(
"ToggleMaster"
)
end
elseif input.KeyCode
== Enum.KeyCode.RightShift
then
G.Gui.Enabled =
not G.Gui.Enabled
if G.Gui.Enabled then
updateGui()
end
elseif input.KeyCode == Enum.KeyCode.F6 then
if G.Command then
G.Command:Fire(
"PrintDebug"
)
end
end
end
)
task.spawn(function()
while task.wait(0.30) do
local success, result =
pcall(updateGui)
if not success then
G.EngineLabel.Text =
"GUI-Fehler: "
.. tostring(result)
G.EngineLabel.TextColor3 =
COLORS.Error
end
end
end)
print("[SMART RNG V5.1 GUI] Geladen.")
print("[SMART RNG V5.1 GUI] Warte auf Engine.")No comments yet for this script.