Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local Math = require(script.StringToMath) -- API to calculate operations from expressions
- local IdRetriever = require(script.IdRetriever) -- API to generate a unique ID for each blackhole (60,466,176 possible IDs from 5 digits)
- local blackHole = script.Parent -- Reference to the black hole
- --------------------------------------------------------------------------------------------------------------------
- -- This version is for purely technical & nerdy purposes. If you know how to code, you can screw around here, lol --
- --------------------------------------------------------------------------------------------------------------------
- local CollectionService = game:GetService("CollectionService")
- -- Returns the first BoolValue tagged with 'tagName' that is a descendant of 'parentInstance'
- local function getTaggedBool(parentInstance, tagName)
- for _, inst in ipairs(CollectionService:GetTagged(tagName)) do
- if inst:IsA("BoolValue") and inst:IsDescendantOf(parentInstance) then
- return inst
- end
- end
- return nil
- end
- -- Assume these are StringValue objects containing expressions
- local GString = blackHole:FindFirstChild("G_(gravity)", true) -- e.g. "6.67e-11"
- local cString = blackHole:FindFirstChild("c_(light_speed)", true) -- e.g. "3e8"
- local customMassString = blackHole:FindFirstChild("CustomMass", true) -- optional string expression
- local customMassEnabled = getTaggedBool(blackHole, "UseCustomMass")
- local useEinsteinian = getTaggedBool(blackHole, "UseEinsteinian")
- -- OverridePhysics Boolean and Magnitude StringValue
- local overridePhysics = getTaggedBool(blackHole, "OverridePhysics")
- local magnitudeString = blackHole:FindFirstChild("Magnitude", true)
- -- Simple absorption
- local absorption = getTaggedBool(blackHole, "CanAbsorb")
- local absorptionConnection = nil
- -- Master switch
- local enableGravity = getTaggedBool(blackHole, "EnableGravity")
- -- Generate a unique identifier for this black hole
- local BHId = IdRetriever.FetchID(5) -- Number represents the length of the ID. longer means more unique IDs (MUST BE INTEGERS)
- -- Ensure blackHole has an attachment for the line force target
- local blackHoleAttachment = blackHole:FindFirstChild("LineForceAttachment", true)
- if not blackHoleAttachment then
- blackHoleAttachment = Instance.new("Attachment")
- blackHoleAttachment.Name = "LineForceAttachment"
- blackHoleAttachment.Parent = blackHole
- end
- -- Helper function: parse a StringValue's expression and update its value
- local function parseAndSetValue(strValue)
- local expr = strValue.Value
- -- Try a direct conversion first
- local numberVal = tonumber(expr)
- if numberVal then
- -- Update the string to the numeral equivalent
- strValue.Value = tostring(numberVal)
- -- Remove error indicator from the name if present
- strValue.Name = string.gsub(strValue.Name, "%s*%[ERROR%]", "")
- return numberVal
- else
- local success, result = pcall(function()
- return Math.com(expr)
- end)
- if success and type(result) == "number" then
- -- Replace the string with the computed number (as a string)
- strValue.Value = tostring(result)
- -- Remove error indicator from the name if present.
- strValue.Name = string.gsub(strValue.Name, "%s*%[ERROR%]", "")
- return result
- else
- if not string.find(strValue.Name, "%[ERROR%]") then
- strValue.Name = strValue.Name .. " [ERROR]"
- strValue.Value = "ERROR - Syntax"
- warn("{" .. BHId .. "} Unrecognized operation. Only accepts: ( ) ^ E * / + - [Example: (2E1)^4/2+2]. Mathematical rules apply!")
- print("{" .. BHId .. "} Accepts pi, tau, phi, e (Euler's Number), sin(), cos(), tan(), asin(), acos(), atan(), csc(), sec(), cot() sinh(), cosh(), tanh(), ceil(), floor() or //, round(), log(), root(), rand(), rng()")
- print("TIER syntax => Trigonometry: sin(num), cos(num), ... tanh(num) | Integers: ceil(num), floor(num) or num//num, round(num) | Exponents: log(baseNum, argNum), root(indexNum, radicandNum) | Randomizers: rand(intMin, intMax), rng(doubleMin, doubleMax)")
- end
- return 0
- end
- end
- end
- -- Helper function to update the black hole's name and its attachment name
- local function updateBlackHoleName()
- local method = useEinsteinian.Value and "Einstein" or "Newton"
- local massType = customMassEnabled.Value and "Custom" or "Current"
- blackHole.Name = "{BHv5} " .. method .. " | " .. massType .. " {" .. BHId .. "}"
- if useEinsteinian.Value then
- blackHoleAttachment.Name = "EinsteinianGravityAttachment"
- else
- blackHoleAttachment.Name = "NewtonianGravityAttachment"
- end
- end
- -- Call it once initially
- updateBlackHoleName()
- local function initBoolValueStatusName(boolValue)
- if not boolValue or not boolValue:IsA("BoolValue") then
- return
- end
- -- Determine base name by stripping any existing status symbol or " (…)" suffix:
- local name = boolValue.Name
- -- 1) Remove any parentheses suffix, e.g. "UseCustomMass (ON)" -> "UseCustomMass"
- name = name:gsub("%s*%b()", "")
- -- 2) Remove any leading or trailing status symbols ● or ○ (in case it was previously named "● Name" or "Name ○")
- name = name:gsub("^%s*[●○]%s*", "") -- strip leading
- name = name:gsub("%s*[●○]%s*$", "") -- strip trailing
- -- Trim any excess whitespace:
- name = name:match("^%s*(.-)%s*$") or name
- local baseName = name
- -- Store baseName in an attribute so renaming won’t lose track of it
- boolValue:SetAttribute("BaseName", baseName)
- -- Function to update the Name according to the current Value, placing status first
- local function updateName()
- local storedBase = boolValue:GetAttribute("BaseName") or baseName
- local status = boolValue.Value and "●" --[[on]] or "○" --[[off]]
- -- Prepend status before the base name:
- boolValue.Name = string.format("%s %s", status, storedBase)
- end
- -- Initial naming
- updateName()
- -- Listen for changes to .Value directly
- boolValue:GetPropertyChangedSignal("Value"):Connect(updateName)
- end
- -- Initialize bool names
- initBoolValueStatusName(absorption)
- initBoolValueStatusName(enableGravity)
- initBoolValueStatusName(overridePhysics)
- initBoolValueStatusName(customMassEnabled)
- initBoolValueStatusName(useEinsteinian)
- -- In case the Bools are replaced
- blackHole.ChildAdded:Connect(function(child)
- if child:IsA("BoolValue") then
- -- Strip any suffix:
- local nameNoSuffix = child.Name:gsub("%s*%b()", "")
- if nameNoSuffix == "CanAbsorb"
- or nameNoSuffix == "EnableGravity"
- or nameNoSuffix == "OverridePhysics"
- or nameNoSuffix == "UseCustomMass"
- or nameNoSuffix == "UseEinsteinian" then
- initBoolValueStatusName(child)
- end
- end
- end)
- useEinsteinian.Changed:Connect(function(newValue)
- updateBlackHoleName()
- if newValue then
- for _, object in pairs(workspace:GetDescendants()) do
- if object:IsA("BasePart") then
- local force = object:FindFirstChild("NewtonianGravity_" .. BHId, true)
- if force then
- if force.Attachment0 then
- force.Attachment0:Destroy()
- end
- force:Destroy()
- end
- end
- end
- else
- for _, object in pairs(workspace:GetDescendants()) do
- if object:IsA("BasePart") then
- local force = object:FindFirstChild("RelativisticGravity_" .. BHId, true)
- if force then
- if force.Attachment0 then
- force.Attachment0:Destroy()
- end
- force:Destroy()
- end
- end
- end
- end
- end)
- customMassEnabled.Changed:Connect(function()
- updateBlackHoleName()
- end)
- -- Function to get the current mass for the black hole
- local function getCurrentMass()
- if customMassEnabled.Value == true and customMassString then
- return parseAndSetValue(customMassString)
- else
- return blackHole:GetMass()
- end
- end
- local G_val = parseAndSetValue(GString)
- local c_val = parseAndSetValue(cString)
- local eventHorizon = 2 * G_val * getCurrentMass() / (c_val^2)
- local function studsToMeters(studs)
- return studs / 4
- end
- local function metersToStuds(meters)
- return meters * 4
- end
- -------------------------------------------------------
- -- Anchor/Unanchor Affected Objects on Parser Errors --
- -------------------------------------------------------
- local function checkAndToggleAnchors()
- local hasError = false
- if string.find(GString.Name, "%[ERROR%]") or string.find(cString.Name, "%[ERROR%]") then
- hasError = true
- end
- if customMassEnabled.Value and customMassString then
- if string.find(customMassString.Name, "%[ERROR%]") then
- hasError = true
- end
- end
- if overridePhysics and overridePhysics.Value and magnitudeString then
- if string.find(magnitudeString.Name, "%[ERROR%]") then
- hasError = true
- end
- end
- for _, object in pairs(workspace:GetDescendants()) do
- if object:IsA("BasePart") then
- local newtonForce = object:FindFirstChild("NewtonianGravity_" .. BHId, true)
- local einsteinForce = object:FindFirstChild("RelativisticGravity_" .. BHId, true)
- if newtonForce or einsteinForce then
- if hasError and not object.Anchored then
- object:SetAttribute("WasUnanchoredByBH", true)
- object.Anchored = true
- elseif not hasError and object:GetAttribute("WasUnanchoredByBH") then
- object:SetAttribute("WasUnanchoredByBH", nil)
- object.Anchored = false
- end
- end
- end
- end
- end
- --------------------------------------------
- -- Update Event Horizon and Check Anchors --
- --------------------------------------------
- local function updateEventHorizon()
- G_val = parseAndSetValue(GString)
- c_val = parseAndSetValue(cString)
- local M_val = getCurrentMass()
- eventHorizon = 2 * G_val * M_val / (c_val^2)
- checkAndToggleAnchors()
- end
- GString.Changed:Connect(updateEventHorizon)
- cString.Changed:Connect(updateEventHorizon)
- if customMassString then
- customMassString.Changed:Connect(updateEventHorizon)
- end
- customMassEnabled.Changed:Connect(function()
- updateBlackHoleName()
- updateEventHorizon()
- end)
- ----------------------------------------------------------
- -- Gravitational Force Functions (with OverridePhysics) --
- ----------------------------------------------------------
- local MIN_FORCE_THRESHOLD = 0.001 -- Essentially zero for Robloxian engines
- local function relativisticGravity(object)
- if not object:IsA("BasePart") or object == blackHole or object.Anchored then
- return
- end
- local r = (blackHole.Position - object.Position).Magnitude
- local r_meters = studsToMeters(r)
- if r_meters <= (blackHole.Size.X / 16) then
- return
- end
- local computedMagnitude = nil
- if overridePhysics and overridePhysics.Value and magnitudeString then
- local overrideForce = parseAndSetValue(magnitudeString)
- computedMagnitude = overrideForce / (r_meters^2)
- else
- local g = (G_val * getCurrentMass()) / (r_meters^2) * (1 - (eventHorizon / r_meters))
- local g_studs = metersToStuds(g)
- computedMagnitude = g_studs * object:GetMass()
- end
- local forceName = "RelativisticGravity_" .. BHId
- local existingForce = object:FindFirstChild(forceName, true)
- if math.abs(computedMagnitude) < MIN_FORCE_THRESHOLD then
- if existingForce then
- if existingForce.Attachment0 then
- existingForce.Attachment0:Destroy()
- end
- existingForce:Destroy()
- end
- return
- end
- local force = existingForce
- if not force then
- force = Instance.new("LineForce")
- force.Name = forceName
- local objectAttachment = Instance.new("Attachment")
- objectAttachment.Name = "EinsteinianGravityAttachment"
- objectAttachment.Parent = object
- force.Attachment0 = objectAttachment
- force.Attachment1 = blackHoleAttachment
- force.ApplyAtCenterOfMass = true
- force.Visible = true
- force.Parent = object
- end
- force.Magnitude = computedMagnitude
- end
- local function newtonianGravity(object)
- if not object:IsA("BasePart") or object == blackHole or object.Anchored then
- return
- end
- local r = (blackHole.Position - object.Position).Magnitude / 4
- local r_meters = studsToMeters(r)
- if r_meters <= 0 then return end
- local computedMagnitude = nil
- if overridePhysics and overridePhysics.Value and magnitudeString then
- local overrideForce = parseAndSetValue(magnitudeString)
- computedMagnitude = overrideForce / (r_meters^2)
- else
- local F = (G_val * getCurrentMass() * object:GetMass()) / (r_meters^2)
- local F_studs = metersToStuds(F)
- computedMagnitude = F_studs
- end
- local forceName = "NewtonianGravity_" .. BHId
- local existingForce = object:FindFirstChild(forceName, true)
- if math.abs(computedMagnitude) < MIN_FORCE_THRESHOLD then
- if existingForce then
- if existingForce.Attachment0 then
- existingForce.Attachment0:Destroy()
- end
- existingForce:Destroy()
- end
- return
- end
- local force = existingForce
- if not force then
- force = Instance.new("LineForce")
- force.Name = forceName
- local objectAttachment = Instance.new("Attachment")
- objectAttachment.Name = "NewtonianGravityAttachment"
- objectAttachment.Parent = object
- force.Attachment0 = objectAttachment
- force.Attachment1 = blackHoleAttachment
- force.ApplyAtCenterOfMass = true
- force.Visible = true
- force.Parent = object
- end
- force.Magnitude = computedMagnitude
- end
- local function removeGravitationalForce(object)
- local newtonForce = object:FindFirstChild("NewtonianGravity_" .. BHId, true)
- if newtonForce then
- if newtonForce.Attachment0 then
- newtonForce.Attachment0:Destroy()
- end
- newtonForce:Destroy()
- end
- local relativisticForce = object:FindFirstChild("RelativisticGravity_" .. BHId, true)
- if relativisticForce then
- if relativisticForce.Attachment0 then
- relativisticForce.Attachment0:Destroy()
- end
- relativisticForce:Destroy()
- end
- end
- if enableGravity then
- enableGravity.Changed:Connect(function(newValue)
- if not newValue then
- for _, object in pairs(workspace:GetDescendants()) do
- if object:IsA("BasePart") then
- removeGravitationalForce(object)
- end
- end
- end
- end)
- end
- ---------------------------------------------------
- -- Apply gravitational effects on each heartbeat --
- ---------------------------------------------------
- game:GetService("RunService").Heartbeat:Connect(function()
- if enableGravity and enableGravity.Value then
- for _, object in pairs(workspace:GetDescendants()) do
- if useEinsteinian.Value then
- relativisticGravity(object)
- else
- newtonianGravity(object)
- end
- end
- else
- for _, object in pairs(workspace:GetDescendants()) do
- if object:IsA("BasePart") then
- removeGravitationalForce(object)
- end
- end
- end
- checkAndToggleAnchors()
- end)
- ---------------------------------
- -- VFX Monitoring (Dont touch) --
- ---------------------------------
- -- Table to store initial highlight colors if a Highlight is present { FillColor = Color3, OutlineColor = Color3 }
- local initialHighlightColors = nil
- -- Capture initial highlight colors if a Highlight exists under VFX
- local function captureInitialHighlightColors()
- local vfx = blackHole:FindFirstChild("VFX", true)
- if vfx then
- local highlight = vfx:FindFirstChild("Highlight", true)
- if highlight and highlight:IsA("Highlight") then
- if not initialHighlightColors then
- initialHighlightColors = {
- FillColor = highlight.FillColor,
- OutlineColor = highlight.OutlineColor,
- }
- end
- end
- end
- end
- -- Check if any monitored value is negative
- local function isNegativeState()
- -- Parse gravity and speed-of-light each time to check current numeric
- local gVal = parseAndSetValue(GString)
- local cVal = parseAndSetValue(cString)
- if gVal < 0 or cVal < 0 then
- return true
- end
- if customMassEnabled and customMassEnabled.Value and customMassString then
- local mVal = parseAndSetValue(customMassString)
- if mVal < 0 then
- return true
- end
- end
- if overridePhysics and overridePhysics.Value and magnitudeString then
- local magVal = parseAndSetValue(magnitudeString)
- if magVal < 0 then
- return true
- end
- end
- return false
- end
- -- Update black hole color (and its Highlight) based on negative-state or normal VFX logic (whitehole)
- local function updateBlackHoleColor()
- -- First: capture initial highlight colors if not already and highlight exists
- captureInitialHighlightColors()
- -- If any monitored value is negative, force white color
- if isNegativeState() then
- -- Black hole body to white
- blackHole.Color = Color3.new(1, 1, 1)
- -- If there's a highlight under VFX, set it to white as well
- local vfx = blackHole:FindFirstChild("VFX", true)
- if vfx then
- local highlight = vfx:FindFirstChild("Highlight", true)
- if highlight and highlight:IsA("Highlight") then
- highlight.FillColor = Color3.new(1, 1, 1)
- highlight.OutlineColor = Color3.new(1, 1, 1)
- end
- end
- return
- end
- -- Otherwise, revert to original/VFX-based logic:
- local vfx = blackHole:FindFirstChild("VFX", true)
- if not vfx then
- -- No highlight VFX: black
- blackHole.Color = Color3.fromRGB(0, 0, 0)
- else
- local highlight = vfx:FindFirstChild("Highlight", true)
- if not highlight or (highlight:IsA("Highlight", true) and not highlight.Enabled) then
- blackHole.Color = Color3.fromRGB(0, 0, 0)
- else
- -- Highlight is present and enabled: themed color
- blackHole.Color = Color3.fromRGB(226, 155, 64)
- -- Restore highlight colors to initial if stored
- if highlight and highlight:IsA("Highlight") and initialHighlightColors then
- highlight.FillColor = initialHighlightColors.FillColor
- highlight.OutlineColor = initialHighlightColors.OutlineColor
- end
- end
- end
- end
- -- When GString or cString change, re-check color
- if GString then
- GString.Changed:Connect(function()
- updateBlackHoleColor()
- end)
- end
- if cString then
- cString.Changed:Connect(function()
- updateBlackHoleColor()
- end)
- end
- -- When customMassString changes, or when toggling customMassEnabled, re-check color
- if customMassString then
- customMassString.Changed:Connect(function()
- updateBlackHoleColor()
- end)
- end
- if customMassEnabled then
- customMassEnabled.Changed:Connect(function()
- updateBlackHoleColor()
- end)
- end
- -- VFX Highlight listeners:
- blackHole.ChildRemoved:Connect(function(child)
- if child.Name == "VFX" then
- updateBlackHoleColor()
- end
- end)
- blackHole.ChildAdded:Connect(function(child)
- if child.Name == "VFX" then
- -- If a new VFX instance is added under blackHole, check for Highlight
- local highlight = child:FindFirstChild("Highlight", true)
- if highlight and highlight:IsA("Highlight") then
- -- Capture its initial colors
- captureInitialHighlightColors()
- -- Listen for Enabled changes
- highlight.Changed:Connect(function(prop)
- if prop == "Enabled" then
- updateBlackHoleColor()
- end
- end)
- end
- updateBlackHoleColor()
- end
- end)
- blackHole.ChildAdded:Connect(function(child)
- if child.Name == "VFX" then
- local highlight = child:FindFirstChild("Highlight", true)
- if highlight and highlight:IsA("Highlight") then
- highlight.Changed:Connect(function(prop)
- if prop == "Enabled" then
- updateBlackHoleColor()
- end
- end)
- end
- updateBlackHoleColor()
- end
- end)
- -- If at startup a VFX Highlight exists, hook its Enabled change
- do
- local vfx = blackHole:FindFirstChild("VFX", true)
- if vfx then
- local highlight = vfx:FindFirstChild("Highlight", true)
- if highlight and highlight:IsA("Highlight") then
- -- Capture initial colors
- captureInitialHighlightColors()
- highlight.Changed:Connect(function(prop)
- if prop == "Enabled" then
- updateBlackHoleColor()
- end
- end)
- end
- end
- end
- -- Initial invocation
- updateBlackHoleColor()
- ---------------------------------------------------------------
- -- Update Absorption Connection with Absorbed Counter Update --
- ---------------------------------------------------------------
- local CollectionService = game:GetService("CollectionService")
- local function updateAbsorptionConnection()
- if absorption.Value then
- if not absorptionConnection then
- absorptionConnection = blackHole.Touched:Connect(function(hit)
- if hit and hit ~= blackHole and not hit.Anchored then
- hit:Destroy()
- -- Find any object inside blackHole with the tag "Absorbed Counter"
- local counterItems = CollectionService:GetTagged("Absorbed Counter")
- for _, counter in ipairs(counterItems) do
- if counter:IsDescendantOf(blackHole) then
- -- Extract the current count from the name in the format "Consumed: <number>"
- local currentValue = tonumber(string.match(counter.Name, "Consumed:%s*(%d+)")) or 0
- -- Update the counter name with the new value
- counter.Name = "Consumed: " .. (currentValue + 1)
- end
- end
- end
- end)
- end
- else
- if absorptionConnection then
- absorptionConnection:Disconnect()
- absorptionConnection = nil
- end
- end
- end
- if absorption then
- absorption.Changed:Connect(updateAbsorptionConnection)
- updateAbsorptionConnection()
- end
- -----------------------------
- -- ID Locking (Dont touch) --
- -----------------------------
- local function enforceIDName(idObj)
- local identity = "ID: " .. BHId
- if idObj.Name ~= identity then
- idObj.Name = identity
- end
- idObj:GetPropertyChangedSignal("Name"):Connect(function()
- if idObj.Name ~= identity then
- idObj.Name = identity
- end
- end)
- end
- for _, idObj in ipairs(CollectionService:GetTagged("ID")) do
- if idObj.Parent == blackHole then
- enforceIDName(idObj)
- end
- end
Advertisement
Comments
-
- -- MathParserModule.lua
- local MathParser = {}
- -- Step 1: Tokenizer
- local function tokenize(expr)
- local tokens = {}
- local i = 1
- while i <= #expr do
- local c = expr:sub(i, i)
- if c:match("%s") then
- i = i + 1
- elseif c:match("%d") or c == '.' then
- local num = ""
- -- Capture the base number (integer or float)
- while i <= #expr and expr:sub(i, i):match("[%d%.]") do
- num = num .. expr:sub(i, i)
- i = i + 1
- end
- -- Check for scientific notation (immediately following without space)
- if i <= #expr and (expr:sub(i, i) == "e" or expr:sub(i, i) == "E") then
- num = num .. expr:sub(i, i)
- i = i + 1
- -- Optional sign after e/E
- if i <= #expr and (expr:sub(i, i) == "+" or expr:sub(i, i) == "-") then
- num = num .. expr:sub(i, i)
- i = i + 1
- end
- -- Append the exponent digits
- while i <= #expr and expr:sub(i, i):match("%d") do
- num = num .. expr:sub(i, i)
- i = i + 1
- end
- end
- table.insert(tokens, {type = "number", value = tonumber(num)})
- elseif c == "(" or c == ")" then
- table.insert(tokens, {type = "paren", value = c})
- i = i + 1
- else
- -- Try to match multi-character operators
- local op = nil
- -- If the operator "E" appears separated by spaces, treat it as an operator token.
- if expr:sub(i, i) == "E" then
- op = "E"
- i = i + 1
- else
- op = c
- i = i + 1
- end
- table.insert(tokens, {type = "operator", value = op})
- end
- end
- return tokens
- end
- -- Operator precedence and associativity
- local operators = {
- ["E"] = {precedence = 5, associativity = "right"}, -- Scientific notation operator
- ["^"] = {precedence = 4, associativity = "right"},
- ["*"] = {precedence = 3, associativity = "left"},
- ["/"] = {precedence = 3, associativity = "left"},
- ["+"] = {precedence = 2, associativity = "left"},
- ["-"] = {precedence = 2, associativity = "left"},
- }
- -- Step 2: Parser using the shunting-yard algorithm
- local function shuntingYard(tokens)
- local output = {}
- local opStack = {}
- for _, token in ipairs(tokens) do
- if token.type == "number" then
- table.insert(output, token)
- elseif token.type == "operator" then
- local o1 = token.value
- while #opStack > 0 do
- local top = opStack[#opStack]
- if top.type ~= "operator" then break end
- local o2 = top.value
- if (operators[o1].associativity == "left" and operators[o1].precedence <= operators[o2].precedence) or
- (operators[o1].associativity == "right" and operators[o1].precedence < operators[o2].precedence) then
- table.insert(output, table.remove(opStack))
- else
- break
- end
- end
- table.insert(opStack, token)
- elseif token.type == "paren" then
- if token.value == "(" then
- table.insert(opStack, token)
- else -- token.value == ")"
- while #opStack > 0 and opStack[#opStack].value ~= "(" do
- table.insert(output, table.remove(opStack))
- end
- if #opStack == 0 then
- error("Mismatched parentheses")
- end
- table.remove(opStack) -- remove "("
- end
- end
- end
- while #opStack > 0 do
- local top = table.remove(opStack)
- if top.value == "(" or top.value == ")" then
- error("Mismatched parentheses")
- end
- table.insert(output, top)
- end
- return output
- end
- -- Step 3: Evaluate Reverse Polish Notation (RPN)
- local function evaluateRPN(rpn)
- local stack = {}
- for _, token in ipairs(rpn) do
- if token.type == "number" then
- table.insert(stack, token.value)
- elseif token.type == "operator" then
- local b = table.remove(stack)
- local a = table.remove(stack)
- local result = nil
- if token.value == "+" then
- result = a + b
- elseif token.value == "-" then
- result = a - b
- elseif token.value == "*" then
- result = a * b
- elseif token.value == "/" then
- result = a / b
- elseif token.value == "^" then
- result = a ^ b
- elseif token.value == "E" then
- -- Evaluate as: a * (10^b)
- result = a * (10 ^ b)
- else
- error("Unknown operator: " .. token.value)
- end
- table.insert(stack, result)
- end
- end
- if #stack ~= 1 then
- error("Invalid expression")
- end
- return stack[1]
- end
- -- The universal parser function that catches errors and returns an error message
- function MathParser.evaluateExpression(expr)
- local tokens = tokenize(expr)
- local status, result = pcall(function()
- local rpn = shuntingYard(tokens)
- return evaluateRPN(rpn)
- end)
- if not status then
- return "Error: " .. result
- else
- return result
- end
- end
- return MathParser
-
- local IDGenerator = {}
- local charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
- function IDGenerator.GenerateID(length)
- local id = ""
- for i = 1, length do
- local randIndex = math.random(#charset)
- id = id .. charset:sub(randIndex, randIndex)
- end
- return id
- end
- return IDGenerator
-
- [ Welcome to Blackhole 5.1 Experimental Branch! ]
- ----------
- Quick Desc
- ----------
- Blackhole 5.1 is a beta version of future blackholes, especially for effecient and accurate gravities!
- This Blackhole version does not support fancy absorptions like previous Blackhole versions, but has significant improvements on gravitation.
- Please do not remove any asset within the Blackhole as they will cause errors!
- ---------------
- Live Parameters
- ---------------
- `G_(gravity)` (1) = Gravitation constant, very sensitive to different values, try 0.1 incrementals.
- `UseCustomMass` (false) = Use custom mass as current mass has restrictions. this does not affect the ACTUAL mass, it's only pseudo.
- `CustomMass` (5236) = The custom mass (default is current mass, but it may not be if resized).
- `UseEinsteinian` (true) = Use Einstein's gravity field equation: (G*M/r^2)*(1-Rs/r) where Rs is the Schwarzschild radius, else use Newtonian: (Gm1m2/r^2).
- `OverridePhysics` (false) = If true, disregard the gravity formulas and simply use (`Magnitude`/r^2), more info on `Magnitude`
- `Magnitude` (1000000) = The gravitational strength. (also typed as 1e6).
- c_(light_speed) (300000000) = Speed of light (used for Schwarzschild radius in Einsteinian gravity, also typed as 3e8).
- Schwarzschild radius = Event Horizon (the actual radius of blackhole), which the script uses to cancel gravity as r->0 outputs infinity.
- ------------
- Math Parsing
- ------------
- Follows mathematical syntax!
- Non boolean parameters can be hard to type for precise and or large values like 300000000 or 12300, but they can be typed as exponentials or scientific notations:
- 1.23e4 = 1.23*(10^4) = 12300
- It may output an error, this is how to use the Math Parsing system:
- Accepts PEDMAS or BEDMAS (Parentheses, Exponents, Division, Multiplication, Addition & Subtraction)
- a^4 = a*a*a*a, for roots: a^(1/2) = square root of a, whatever number in a^(1/n) is the n-th root.
- a/b, but b =/= 0.
- a*b...
- etc.
- Example:
- (1e2)^(3/4)+5-6 = 30.62...
Add Comment
Please, Sign In to add comment
Advertisement