Advertisement
1m1m0

Project Einstein: BHv5 Branch

Apr 4th, 2025 (edited)
365
0
Never
3
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 21.04 KB | None | 0 0
  1. local Math = require(script.StringToMath) -- API to calculate operations from expressions
  2. local IdRetriever = require(script.IdRetriever) -- API to generate a unique ID for each blackhole (60,466,176 possible IDs from 5 digits)
  3. local blackHole = script.Parent -- Reference to the black hole
  4.  
  5. --------------------------------------------------------------------------------------------------------------------
  6. -- This version is for purely technical & nerdy purposes. If you know how to code, you can screw around here, lol --
  7. --------------------------------------------------------------------------------------------------------------------
  8.  
  9. local CollectionService = game:GetService("CollectionService")
  10.  
  11. -- Returns the first BoolValue tagged with 'tagName' that is a descendant of 'parentInstance'
  12. local function getTaggedBool(parentInstance, tagName)
  13.     for _, inst in ipairs(CollectionService:GetTagged(tagName)) do
  14.         if inst:IsA("BoolValue") and inst:IsDescendantOf(parentInstance) then
  15.             return inst
  16.         end
  17.     end
  18.     return nil
  19. end
  20.  
  21. -- Assume these are StringValue objects containing expressions
  22. local GString = blackHole:FindFirstChild("G_(gravity)", true) -- e.g. "6.67e-11"
  23. local cString = blackHole:FindFirstChild("c_(light_speed)", true) -- e.g. "3e8"
  24. local customMassString = blackHole:FindFirstChild("CustomMass", true) -- optional string expression
  25.  
  26. local customMassEnabled = getTaggedBool(blackHole, "UseCustomMass")
  27. local useEinsteinian = getTaggedBool(blackHole, "UseEinsteinian")
  28.  
  29. -- OverridePhysics Boolean and Magnitude StringValue
  30. local overridePhysics = getTaggedBool(blackHole, "OverridePhysics")
  31. local magnitudeString = blackHole:FindFirstChild("Magnitude", true)
  32.  
  33. -- Simple absorption
  34. local absorption = getTaggedBool(blackHole, "CanAbsorb")
  35. local absorptionConnection = nil
  36.  
  37. -- Master switch
  38. local enableGravity = getTaggedBool(blackHole, "EnableGravity")
  39.  
  40. -- Generate a unique identifier for this black hole
  41. local BHId = IdRetriever.FetchID(5) -- Number represents the length of the ID. longer means more unique IDs (MUST BE INTEGERS)
  42.  
  43. -- Ensure blackHole has an attachment for the line force target
  44. local blackHoleAttachment = blackHole:FindFirstChild("LineForceAttachment", true)
  45. if not blackHoleAttachment then
  46.     blackHoleAttachment = Instance.new("Attachment")
  47.     blackHoleAttachment.Name = "LineForceAttachment"
  48.     blackHoleAttachment.Parent = blackHole
  49. end
  50.  
  51. -- Helper function: parse a StringValue's expression and update its value
  52. local function parseAndSetValue(strValue)
  53.     local expr = strValue.Value
  54.     -- Try a direct conversion first
  55.     local numberVal = tonumber(expr)
  56.     if numberVal then
  57.         -- Update the string to the numeral equivalent
  58.         strValue.Value = tostring(numberVal)
  59.         -- Remove error indicator from the name if present
  60.         strValue.Name = string.gsub(strValue.Name, "%s*%[ERROR%]", "")
  61.         return numberVal
  62.     else
  63.         local success, result = pcall(function()
  64.             return Math.com(expr)
  65.         end)
  66.         if success and type(result) == "number" then
  67.             -- Replace the string with the computed number (as a string)
  68.             strValue.Value = tostring(result)
  69.             -- Remove error indicator from the name if present.
  70.             strValue.Name = string.gsub(strValue.Name, "%s*%[ERROR%]", "")
  71.             return result
  72.         else
  73.             if not string.find(strValue.Name, "%[ERROR%]") then
  74.                 strValue.Name = strValue.Name .. " [ERROR]"
  75.                 strValue.Value = "ERROR - Syntax"
  76.                 warn("{" .. BHId .. "} Unrecognized operation. Only accepts: ( ) ^ E * / + - [Example: (2E1)^4/2+2]. Mathematical rules apply!")
  77.                 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()")
  78.                 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)")
  79.             end
  80.             return 0
  81.         end
  82.     end
  83. end
  84.  
  85. -- Helper function to update the black hole's name and its attachment name
  86. local function updateBlackHoleName()
  87.     local method = useEinsteinian.Value and "Einstein" or "Newton"
  88.     local massType = customMassEnabled.Value and "Custom" or "Current"
  89.     blackHole.Name = "{BHv5} " .. method .. " | " .. massType .. " {" .. BHId .. "}"
  90.     if useEinsteinian.Value then
  91.         blackHoleAttachment.Name = "EinsteinianGravityAttachment"
  92.     else
  93.         blackHoleAttachment.Name = "NewtonianGravityAttachment"
  94.     end
  95. end
  96.  
  97. -- Call it once initially
  98. updateBlackHoleName()
  99.  
  100. local function initBoolValueStatusName(boolValue)
  101.     if not boolValue or not boolValue:IsA("BoolValue") then
  102.         return
  103.     end
  104.  
  105.     -- Determine base name by stripping any existing status symbol or " (…)" suffix:
  106.     local name = boolValue.Name
  107.     -- 1) Remove any parentheses suffix, e.g. "UseCustomMass (ON)" -> "UseCustomMass"
  108.     name = name:gsub("%s*%b()", "")
  109.     -- 2) Remove any leading or trailing status symbols ● or ○ (in case it was previously named "● Name" or "Name ○")
  110.     name = name:gsub("^%s*[●○]%s*", "") -- strip leading
  111.     name = name:gsub("%s*[●○]%s*$", "") -- strip trailing
  112.     -- Trim any excess whitespace:
  113.     name = name:match("^%s*(.-)%s*$") or name
  114.  
  115.     local baseName = name
  116.     -- Store baseName in an attribute so renaming won’t lose track of it
  117.     boolValue:SetAttribute("BaseName", baseName)
  118.  
  119.     -- Function to update the Name according to the current Value, placing status first
  120.     local function updateName()
  121.         local storedBase = boolValue:GetAttribute("BaseName") or baseName
  122.         local status = boolValue.Value and "●" --[[on]] or "○" --[[off]]
  123.         -- Prepend status before the base name:
  124.         boolValue.Name = string.format("%s %s", status, storedBase)
  125.     end
  126.  
  127.     -- Initial naming
  128.     updateName()
  129.  
  130.     -- Listen for changes to .Value directly
  131.     boolValue:GetPropertyChangedSignal("Value"):Connect(updateName)
  132. end
  133.  
  134. -- Initialize bool names
  135. initBoolValueStatusName(absorption)
  136. initBoolValueStatusName(enableGravity)
  137. initBoolValueStatusName(overridePhysics)
  138. initBoolValueStatusName(customMassEnabled)
  139. initBoolValueStatusName(useEinsteinian)
  140.  
  141. -- In case the Bools are replaced
  142. blackHole.ChildAdded:Connect(function(child)
  143.     if child:IsA("BoolValue") then
  144.         -- Strip any suffix:
  145.         local nameNoSuffix = child.Name:gsub("%s*%b()", "")
  146.         if nameNoSuffix == "CanAbsorb"
  147.             or nameNoSuffix == "EnableGravity"
  148.             or nameNoSuffix == "OverridePhysics"
  149.             or nameNoSuffix == "UseCustomMass"
  150.             or nameNoSuffix == "UseEinsteinian" then
  151.             initBoolValueStatusName(child)
  152.         end
  153.     end
  154. end)
  155.  
  156. useEinsteinian.Changed:Connect(function(newValue)
  157.     updateBlackHoleName()
  158.     if newValue then
  159.         for _, object in pairs(workspace:GetDescendants()) do
  160.             if object:IsA("BasePart") then
  161.                 local force = object:FindFirstChild("NewtonianGravity_" .. BHId, true)
  162.                 if force then
  163.                     if force.Attachment0 then
  164.                         force.Attachment0:Destroy()
  165.                     end
  166.                     force:Destroy()
  167.                 end
  168.             end
  169.         end
  170.     else
  171.         for _, object in pairs(workspace:GetDescendants()) do
  172.             if object:IsA("BasePart") then
  173.                 local force = object:FindFirstChild("RelativisticGravity_" .. BHId, true)
  174.                 if force then
  175.                     if force.Attachment0 then
  176.                         force.Attachment0:Destroy()
  177.                     end
  178.                     force:Destroy()
  179.                 end
  180.             end
  181.         end
  182.     end
  183. end)
  184.  
  185. customMassEnabled.Changed:Connect(function()
  186.     updateBlackHoleName()
  187. end)
  188.  
  189. -- Function to get the current mass for the black hole
  190. local function getCurrentMass()
  191.     if customMassEnabled.Value == true and customMassString then
  192.         return parseAndSetValue(customMassString)
  193.     else
  194.         return blackHole:GetMass()
  195.     end
  196. end
  197.  
  198. local G_val = parseAndSetValue(GString)
  199. local c_val = parseAndSetValue(cString)
  200.  
  201. local eventHorizon = 2 * G_val * getCurrentMass() / (c_val^2)
  202.  
  203. local function studsToMeters(studs)
  204.     return studs / 4
  205. end
  206.  
  207. local function metersToStuds(meters)
  208.     return meters * 4
  209. end
  210.  
  211. -------------------------------------------------------
  212. -- Anchor/Unanchor Affected Objects on Parser Errors --
  213. -------------------------------------------------------
  214. local function checkAndToggleAnchors()
  215.     local hasError = false
  216.     if string.find(GString.Name, "%[ERROR%]") or string.find(cString.Name, "%[ERROR%]") then
  217.         hasError = true
  218.     end
  219.     if customMassEnabled.Value and customMassString then
  220.         if string.find(customMassString.Name, "%[ERROR%]") then
  221.             hasError = true
  222.         end
  223.     end
  224.     if overridePhysics and overridePhysics.Value and magnitudeString then
  225.         if string.find(magnitudeString.Name, "%[ERROR%]") then
  226.             hasError = true
  227.         end
  228.     end
  229.  
  230.     for _, object in pairs(workspace:GetDescendants()) do
  231.         if object:IsA("BasePart") then
  232.             local newtonForce = object:FindFirstChild("NewtonianGravity_" .. BHId, true)
  233.             local einsteinForce = object:FindFirstChild("RelativisticGravity_" .. BHId, true)
  234.             if newtonForce or einsteinForce then
  235.                 if hasError and not object.Anchored then
  236.                     object:SetAttribute("WasUnanchoredByBH", true)
  237.                     object.Anchored = true
  238.                 elseif not hasError and object:GetAttribute("WasUnanchoredByBH") then
  239.                     object:SetAttribute("WasUnanchoredByBH", nil)
  240.                     object.Anchored = false
  241.                 end
  242.             end
  243.         end
  244.     end
  245. end
  246.  
  247. --------------------------------------------
  248. -- Update Event Horizon and Check Anchors --
  249. --------------------------------------------
  250. local function updateEventHorizon()
  251.     G_val = parseAndSetValue(GString)
  252.     c_val = parseAndSetValue(cString)
  253.     local M_val = getCurrentMass()
  254.     eventHorizon = 2 * G_val * M_val / (c_val^2)
  255.     checkAndToggleAnchors()
  256. end
  257.  
  258. GString.Changed:Connect(updateEventHorizon)
  259. cString.Changed:Connect(updateEventHorizon)
  260. if customMassString then
  261.     customMassString.Changed:Connect(updateEventHorizon)
  262. end
  263.  
  264. customMassEnabled.Changed:Connect(function()
  265.     updateBlackHoleName()
  266.     updateEventHorizon()
  267. end)
  268.  
  269. ----------------------------------------------------------
  270. -- Gravitational Force Functions (with OverridePhysics) --
  271. ----------------------------------------------------------
  272.  
  273. local MIN_FORCE_THRESHOLD = 0.001 -- Essentially zero for Robloxian engines
  274.  
  275. local function relativisticGravity(object)
  276.     if not object:IsA("BasePart") or object == blackHole or object.Anchored then
  277.         return
  278.     end
  279.  
  280.     local r = (blackHole.Position - object.Position).Magnitude
  281.     local r_meters = studsToMeters(r)
  282.     if r_meters <= (blackHole.Size.X / 16) then
  283.         return
  284.     end
  285.  
  286.     local computedMagnitude = nil
  287.     if overridePhysics and overridePhysics.Value and magnitudeString then
  288.         local overrideForce = parseAndSetValue(magnitudeString)
  289.         computedMagnitude = overrideForce / (r_meters^2)
  290.     else
  291.         local g = (G_val * getCurrentMass()) / (r_meters^2) * (1 - (eventHorizon / r_meters))
  292.         local g_studs = metersToStuds(g)
  293.         computedMagnitude = g_studs * object:GetMass()
  294.     end
  295.  
  296.     local forceName = "RelativisticGravity_" .. BHId
  297.     local existingForce = object:FindFirstChild(forceName, true)
  298.     if math.abs(computedMagnitude) < MIN_FORCE_THRESHOLD then
  299.         if existingForce then
  300.             if existingForce.Attachment0 then
  301.                 existingForce.Attachment0:Destroy()
  302.             end
  303.             existingForce:Destroy()
  304.         end
  305.         return
  306.     end
  307.  
  308.     local force = existingForce
  309.     if not force then
  310.         force = Instance.new("LineForce")
  311.         force.Name = forceName
  312.         local objectAttachment = Instance.new("Attachment")
  313.         objectAttachment.Name = "EinsteinianGravityAttachment"
  314.         objectAttachment.Parent = object
  315.         force.Attachment0 = objectAttachment
  316.         force.Attachment1 = blackHoleAttachment
  317.         force.ApplyAtCenterOfMass = true
  318.         force.Visible = true
  319.         force.Parent = object
  320.     end
  321.  
  322.     force.Magnitude = computedMagnitude
  323. end
  324.  
  325. local function newtonianGravity(object)
  326.     if not object:IsA("BasePart") or object == blackHole or object.Anchored then
  327.         return
  328.     end
  329.  
  330.     local r = (blackHole.Position - object.Position).Magnitude / 4
  331.     local r_meters = studsToMeters(r)
  332.     if r_meters <= 0 then return end
  333.  
  334.     local computedMagnitude = nil
  335.     if overridePhysics and overridePhysics.Value and magnitudeString then
  336.         local overrideForce = parseAndSetValue(magnitudeString)
  337.         computedMagnitude = overrideForce / (r_meters^2)
  338.     else
  339.         local F = (G_val * getCurrentMass() * object:GetMass()) / (r_meters^2)
  340.         local F_studs = metersToStuds(F)
  341.         computedMagnitude = F_studs
  342.     end
  343.  
  344.     local forceName = "NewtonianGravity_" .. BHId
  345.     local existingForce = object:FindFirstChild(forceName, true)
  346.     if math.abs(computedMagnitude) < MIN_FORCE_THRESHOLD then
  347.         if existingForce then
  348.             if existingForce.Attachment0 then
  349.                 existingForce.Attachment0:Destroy()
  350.             end
  351.             existingForce:Destroy()
  352.         end
  353.         return
  354.     end
  355.  
  356.     local force = existingForce
  357.     if not force then
  358.         force = Instance.new("LineForce")
  359.         force.Name = forceName
  360.         local objectAttachment = Instance.new("Attachment")
  361.         objectAttachment.Name = "NewtonianGravityAttachment"
  362.         objectAttachment.Parent = object
  363.         force.Attachment0 = objectAttachment
  364.         force.Attachment1 = blackHoleAttachment
  365.         force.ApplyAtCenterOfMass = true
  366.         force.Visible = true
  367.         force.Parent = object
  368.     end
  369.  
  370.     force.Magnitude = computedMagnitude
  371. end
  372.  
  373. local function removeGravitationalForce(object)
  374.     local newtonForce = object:FindFirstChild("NewtonianGravity_" .. BHId, true)
  375.     if newtonForce then
  376.         if newtonForce.Attachment0 then
  377.             newtonForce.Attachment0:Destroy()
  378.         end
  379.         newtonForce:Destroy()
  380.     end
  381.  
  382.     local relativisticForce = object:FindFirstChild("RelativisticGravity_" .. BHId, true)
  383.     if relativisticForce then
  384.         if relativisticForce.Attachment0 then
  385.             relativisticForce.Attachment0:Destroy()
  386.         end
  387.         relativisticForce:Destroy()
  388.     end
  389. end
  390.  
  391. if enableGravity then
  392.     enableGravity.Changed:Connect(function(newValue)
  393.         if not newValue then
  394.             for _, object in pairs(workspace:GetDescendants()) do
  395.                 if object:IsA("BasePart") then
  396.                     removeGravitationalForce(object)
  397.                 end
  398.             end
  399.         end
  400.     end)
  401. end
  402.  
  403. ---------------------------------------------------
  404. -- Apply gravitational effects on each heartbeat --
  405. ---------------------------------------------------
  406. game:GetService("RunService").Heartbeat:Connect(function()
  407.     if enableGravity and enableGravity.Value then
  408.         for _, object in pairs(workspace:GetDescendants()) do
  409.             if useEinsteinian.Value then
  410.                 relativisticGravity(object)
  411.             else
  412.                 newtonianGravity(object)
  413.             end
  414.         end
  415.     else
  416.         for _, object in pairs(workspace:GetDescendants()) do
  417.             if object:IsA("BasePart") then
  418.                 removeGravitationalForce(object)
  419.             end
  420.         end
  421.     end
  422.  
  423.     checkAndToggleAnchors()
  424. end)
  425.  
  426. ---------------------------------
  427. -- VFX Monitoring (Dont touch) --
  428. ---------------------------------
  429.  
  430. -- Table to store initial highlight colors if a Highlight is present { FillColor = Color3, OutlineColor = Color3 }
  431. local initialHighlightColors = nil
  432.  
  433. -- Capture initial highlight colors if a Highlight exists under VFX
  434. local function captureInitialHighlightColors()
  435.     local vfx = blackHole:FindFirstChild("VFX", true)
  436.     if vfx then
  437.         local highlight = vfx:FindFirstChild("Highlight", true)
  438.         if highlight and highlight:IsA("Highlight") then
  439.             if not initialHighlightColors then
  440.                 initialHighlightColors = {
  441.                     FillColor = highlight.FillColor,
  442.                     OutlineColor = highlight.OutlineColor,
  443.                 }
  444.             end
  445.         end
  446.     end
  447. end
  448.  
  449. -- Check if any monitored value is negative
  450. local function isNegativeState()
  451.     -- Parse gravity and speed-of-light each time to check current numeric
  452.     local gVal = parseAndSetValue(GString)
  453.     local cVal = parseAndSetValue(cString)
  454.     if gVal < 0 or cVal < 0 then
  455.         return true
  456.     end
  457.     if customMassEnabled and customMassEnabled.Value and customMassString then
  458.         local mVal = parseAndSetValue(customMassString)
  459.         if mVal < 0 then
  460.             return true
  461.         end
  462.     end
  463.     if overridePhysics and overridePhysics.Value and magnitudeString then
  464.         local magVal = parseAndSetValue(magnitudeString)
  465.         if magVal < 0 then
  466.             return true
  467.         end
  468.     end
  469.     return false
  470. end
  471.  
  472. -- Update black hole color (and its Highlight) based on negative-state or normal VFX logic (whitehole)
  473. local function updateBlackHoleColor()
  474.     -- First: capture initial highlight colors if not already and highlight exists
  475.     captureInitialHighlightColors()
  476.  
  477.     -- If any monitored value is negative, force white color
  478.     if isNegativeState() then
  479.         -- Black hole body to white
  480.         blackHole.Color = Color3.new(1, 1, 1)
  481.         -- If there's a highlight under VFX, set it to white as well
  482.         local vfx = blackHole:FindFirstChild("VFX", true)
  483.         if vfx then
  484.             local highlight = vfx:FindFirstChild("Highlight", true)
  485.             if highlight and highlight:IsA("Highlight") then
  486.                 highlight.FillColor = Color3.new(1, 1, 1)
  487.                 highlight.OutlineColor = Color3.new(1, 1, 1)
  488.             end
  489.         end
  490.         return
  491.     end
  492.  
  493.     -- Otherwise, revert to original/VFX-based logic:
  494.     local vfx = blackHole:FindFirstChild("VFX", true)
  495.     if not vfx then
  496.         -- No highlight VFX: black
  497.         blackHole.Color = Color3.fromRGB(0, 0, 0)
  498.     else
  499.         local highlight = vfx:FindFirstChild("Highlight", true)
  500.         if not highlight or (highlight:IsA("Highlight", true) and not highlight.Enabled) then
  501.             blackHole.Color = Color3.fromRGB(0, 0, 0)
  502.         else
  503.             -- Highlight is present and enabled: themed color
  504.             blackHole.Color = Color3.fromRGB(226, 155, 64)
  505.             -- Restore highlight colors to initial if stored
  506.             if highlight and highlight:IsA("Highlight") and initialHighlightColors then
  507.                 highlight.FillColor = initialHighlightColors.FillColor
  508.                 highlight.OutlineColor = initialHighlightColors.OutlineColor
  509.             end
  510.         end
  511.     end
  512. end
  513.  
  514. -- When GString or cString change, re-check color
  515. if GString then
  516.     GString.Changed:Connect(function()
  517.         updateBlackHoleColor()
  518.     end)
  519. end
  520. if cString then
  521.     cString.Changed:Connect(function()
  522.         updateBlackHoleColor()
  523.     end)
  524. end
  525.  
  526. -- When customMassString changes, or when toggling customMassEnabled, re-check color
  527. if customMassString then
  528.     customMassString.Changed:Connect(function()
  529.         updateBlackHoleColor()
  530.     end)
  531. end
  532. if customMassEnabled then
  533.     customMassEnabled.Changed:Connect(function()
  534.         updateBlackHoleColor()
  535.     end)
  536. end
  537.  
  538. -- VFX Highlight listeners:
  539. blackHole.ChildRemoved:Connect(function(child)
  540.     if child.Name == "VFX" then
  541.         updateBlackHoleColor()
  542.     end
  543. end)
  544.  
  545. blackHole.ChildAdded:Connect(function(child)
  546.     if child.Name == "VFX" then
  547.         -- If a new VFX instance is added under blackHole, check for Highlight
  548.         local highlight = child:FindFirstChild("Highlight", true)
  549.         if highlight and highlight:IsA("Highlight") then
  550.             -- Capture its initial colors
  551.             captureInitialHighlightColors()
  552.             -- Listen for Enabled changes
  553.             highlight.Changed:Connect(function(prop)
  554.                 if prop == "Enabled" then
  555.                     updateBlackHoleColor()
  556.                 end
  557.             end)
  558.         end
  559.         updateBlackHoleColor()
  560.     end
  561. end)
  562.  
  563. blackHole.ChildAdded:Connect(function(child)
  564.     if child.Name == "VFX" then
  565.         local highlight = child:FindFirstChild("Highlight", true)
  566.         if highlight and highlight:IsA("Highlight") then
  567.             highlight.Changed:Connect(function(prop)
  568.                 if prop == "Enabled" then
  569.                     updateBlackHoleColor()
  570.                 end
  571.             end)
  572.         end
  573.         updateBlackHoleColor()
  574.     end
  575. end)
  576.  
  577. -- If at startup a VFX Highlight exists, hook its Enabled change
  578. do
  579.     local vfx = blackHole:FindFirstChild("VFX", true)
  580.     if vfx then
  581.         local highlight = vfx:FindFirstChild("Highlight", true)
  582.         if highlight and highlight:IsA("Highlight") then
  583.             -- Capture initial colors
  584.             captureInitialHighlightColors()
  585.             highlight.Changed:Connect(function(prop)
  586.                 if prop == "Enabled" then
  587.                     updateBlackHoleColor()
  588.                 end
  589.             end)
  590.         end
  591.     end
  592. end
  593.  
  594. -- Initial invocation
  595. updateBlackHoleColor()
  596.  
  597. ---------------------------------------------------------------
  598. -- Update Absorption Connection with Absorbed Counter Update --
  599. ---------------------------------------------------------------
  600. local CollectionService = game:GetService("CollectionService")
  601.  
  602. local function updateAbsorptionConnection()
  603.     if absorption.Value then
  604.         if not absorptionConnection then
  605.             absorptionConnection = blackHole.Touched:Connect(function(hit)
  606.                 if hit and hit ~= blackHole and not hit.Anchored then
  607.                     hit:Destroy()
  608.                     -- Find any object inside blackHole with the tag "Absorbed Counter"
  609.                     local counterItems = CollectionService:GetTagged("Absorbed Counter")
  610.                     for _, counter in ipairs(counterItems) do
  611.                         if counter:IsDescendantOf(blackHole) then
  612.                             -- Extract the current count from the name in the format "Consumed: <number>"
  613.                             local currentValue = tonumber(string.match(counter.Name, "Consumed:%s*(%d+)")) or 0
  614.                             -- Update the counter name with the new value
  615.                             counter.Name = "Consumed: " .. (currentValue + 1)
  616.                         end
  617.                     end
  618.                 end
  619.             end)
  620.         end
  621.     else
  622.         if absorptionConnection then
  623.             absorptionConnection:Disconnect()
  624.             absorptionConnection = nil
  625.         end
  626.     end
  627. end
  628.  
  629. if absorption then
  630.     absorption.Changed:Connect(updateAbsorptionConnection)
  631.     updateAbsorptionConnection()
  632. end
  633.  
  634. -----------------------------
  635. -- ID Locking (Dont touch) --
  636. -----------------------------
  637. local function enforceIDName(idObj)
  638.     local identity = "ID: " .. BHId
  639.     if idObj.Name ~= identity then
  640.         idObj.Name = identity
  641.     end
  642.     idObj:GetPropertyChangedSignal("Name"):Connect(function()
  643.         if idObj.Name ~= identity then
  644.             idObj.Name = identity
  645.         end
  646.     end)
  647. end
  648.  
  649. for _, idObj in ipairs(CollectionService:GetTagged("ID")) do
  650.     if idObj.Parent == blackHole then
  651.         enforceIDName(idObj)
  652.     end
  653. end
Advertisement
Comments
  • 1m1m0
    93 days
    # Lua 4.47 KB | 0 0
    1. -- MathParserModule.lua
    2.  
    3. local MathParser = {}
    4.  
    5. -- Step 1: Tokenizer
    6. local function tokenize(expr)
    7.     local tokens = {}
    8.     local i = 1
    9.     while i <= #expr do
    10.         local c = expr:sub(i, i)
    11.         if c:match("%s") then
    12.             i = i + 1
    13.         elseif c:match("%d") or c == '.' then
    14.             local num = ""
    15.             -- Capture the base number (integer or float)
    16.             while i <= #expr and expr:sub(i, i):match("[%d%.]") do
    17.                 num = num .. expr:sub(i, i)
    18.                 i = i + 1
    19.             end
    20.             -- Check for scientific notation (immediately following without space)
    21.             if i <= #expr and (expr:sub(i, i) == "e" or expr:sub(i, i) == "E") then
    22.                 num = num .. expr:sub(i, i)
    23.                 i = i + 1
    24.                 -- Optional sign after e/E
    25.                 if i <= #expr and (expr:sub(i, i) == "+" or expr:sub(i, i) == "-") then
    26.                     num = num .. expr:sub(i, i)
    27.                     i = i + 1
    28.                 end
    29.                 -- Append the exponent digits
    30.                 while i <= #expr and expr:sub(i, i):match("%d") do
    31.                     num = num .. expr:sub(i, i)
    32.                     i = i + 1
    33.                 end
    34.             end
    35.             table.insert(tokens, {type = "number", value = tonumber(num)})
    36.         elseif c == "(" or c == ")" then
    37.             table.insert(tokens, {type = "paren", value = c})
    38.             i = i + 1
    39.         else
    40.             -- Try to match multi-character operators
    41.             local op = nil
    42.             -- If the operator "E" appears separated by spaces, treat it as an operator token.
    43.             if expr:sub(i, i) == "E" then
    44.                 op = "E"
    45.                 i = i + 1
    46.             else
    47.                 op = c
    48.                 i = i + 1
    49.             end
    50.             table.insert(tokens, {type = "operator", value = op})
    51.         end
    52.     end
    53.     return tokens
    54. end
    55.  
    56. -- Operator precedence and associativity
    57. local operators = {
    58.     ["E"] = {precedence = 5, associativity = "right"}, -- Scientific notation operator
    59.     ["^"] = {precedence = 4, associativity = "right"},
    60.     ["*"] = {precedence = 3, associativity = "left"},
    61.     ["/"] = {precedence = 3, associativity = "left"},
    62.     ["+"] = {precedence = 2, associativity = "left"},
    63.     ["-"] = {precedence = 2, associativity = "left"},
    64. }
    65.  
    66. -- Step 2: Parser using the shunting-yard algorithm
    67. local function shuntingYard(tokens)
    68.     local output = {}
    69.     local opStack = {}
    70.  
    71.     for _, token in ipairs(tokens) do
    72.         if token.type == "number" then
    73.             table.insert(output, token)
    74.         elseif token.type == "operator" then
    75.             local o1 = token.value
    76.             while #opStack > 0 do
    77.                 local top = opStack[#opStack]
    78.                 if top.type ~= "operator" then break end
    79.                 local o2 = top.value
    80.                 if (operators[o1].associativity == "left" and operators[o1].precedence <= operators[o2].precedence) or
    81.                     (operators[o1].associativity == "right" and operators[o1].precedence < operators[o2].precedence) then
    82.                     table.insert(output, table.remove(opStack))
    83.                 else
    84.                     break
    85.                 end
    86.             end
    87.             table.insert(opStack, token)
    88.         elseif token.type == "paren" then
    89.             if token.value == "(" then
    90.                 table.insert(opStack, token)
    91.             else  -- token.value == ")"
    92.                 while #opStack > 0 and opStack[#opStack].value ~= "(" do
    93.                     table.insert(output, table.remove(opStack))
    94.                 end
    95.                 if #opStack == 0 then
    96.                     error("Mismatched parentheses")
    97.                 end
    98.                 table.remove(opStack) -- remove "("
    99.             end
    100.         end
    101.     end
    102.  
    103.     while #opStack > 0 do
    104.         local top = table.remove(opStack)
    105.         if top.value == "(" or top.value == ")" then
    106.             error("Mismatched parentheses")
    107.         end
    108.         table.insert(output, top)
    109.     end
    110.  
    111.     return output
    112. end
    113.  
    114. -- Step 3: Evaluate Reverse Polish Notation (RPN)
    115. local function evaluateRPN(rpn)
    116.     local stack = {}
    117.     for _, token in ipairs(rpn) do
    118.         if token.type == "number" then
    119.             table.insert(stack, token.value)
    120.         elseif token.type == "operator" then
    121.             local b = table.remove(stack)
    122.             local a = table.remove(stack)
    123.             local result = nil
    124.             if token.value == "+" then
    125.                 result = a + b
    126.             elseif token.value == "-" then
    127.                 result = a - b
    128.             elseif token.value == "*" then
    129.                 result = a * b
    130.             elseif token.value == "/" then
    131.                 result = a / b
    132.             elseif token.value == "^" then
    133.                 result = a ^ b
    134.             elseif token.value == "E" then
    135.                 -- Evaluate as: a * (10^b)
    136.                 result = a * (10 ^ b)
    137.             else
    138.                 error("Unknown operator: " .. token.value)
    139.             end
    140.             table.insert(stack, result)
    141.         end
    142.     end
    143.     if #stack ~= 1 then
    144.         error("Invalid expression")
    145.     end
    146.     return stack[1]
    147. end
    148.  
    149. -- The universal parser function that catches errors and returns an error message
    150. function MathParser.evaluateExpression(expr)
    151.     local tokens = tokenize(expr)
    152.     local status, result = pcall(function()
    153.         local rpn = shuntingYard(tokens)
    154.         return evaluateRPN(rpn)
    155.     end)
    156.     if not status then
    157.         return "Error: " .. result
    158.     else
    159.         return result
    160.     end
    161. end
    162.  
    163. return MathParser
    164.  
  • 1m1m0
    93 days
    # Lua 0.29 KB | 0 0
    1. local IDGenerator = {}
    2.  
    3. local charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    4.  
    5. function IDGenerator.GenerateID(length)
    6.     local id = ""
    7.     for i = 1, length do
    8.         local randIndex = math.random(#charset)
    9.         id = id .. charset:sub(randIndex, randIndex)
    10.     end
    11.     return id
    12. end
    13.  
    14. return IDGenerator
    15.  
  • 1m1m0
    89 days
    # text 2.04 KB | 0 0
    1. [ Welcome to Blackhole 5.1 Experimental Branch! ]
    2.  
    3. ----------
    4. Quick Desc
    5. ----------
    6.  
    7. Blackhole 5.1 is a beta version of future blackholes, especially for effecient and accurate gravities!
    8. This Blackhole version does not support fancy absorptions like previous Blackhole versions, but has significant improvements on gravitation.
    9.  
    10. Please do not remove any asset within the Blackhole as they will cause errors!
    11.  
    12. ---------------
    13. Live Parameters
    14. ---------------
    15.  
    16. `G_(gravity)` (1) = Gravitation constant, very sensitive to different values, try 0.1 incrementals.
    17. `UseCustomMass` (false) = Use custom mass as current mass has restrictions. this does not affect the ACTUAL mass, it's only pseudo.
    18. `CustomMass` (5236) = The custom mass (default is current mass, but it may not be if resized).
    19. `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).
    20. `OverridePhysics` (false) = If true, disregard the gravity formulas and simply use (`Magnitude`/r^2), more info on `Magnitude`
    21. `Magnitude` (1000000) = The gravitational strength. (also typed as 1e6).
    22. c_(light_speed) (300000000) = Speed of light (used for Schwarzschild radius in Einsteinian gravity, also typed as 3e8).
    23.  
    24. Schwarzschild radius = Event Horizon (the actual radius of blackhole), which the script uses to cancel gravity as r->0 outputs infinity.
    25.  
    26. ------------
    27. Math Parsing
    28. ------------
    29.  
    30. Follows mathematical syntax!
    31.  
    32. 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:
    33. 1.23e4 = 1.23*(10^4) = 12300
    34.  
    35. It may output an error, this is how to use the Math Parsing system:
    36. Accepts PEDMAS or BEDMAS (Parentheses, Exponents, Division, Multiplication, Addition & Subtraction)
    37. 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.
    38. a/b, but b =/= 0.
    39. a*b...
    40. etc.
    41.  
    42. Example:
    43. (1e2)^(3/4)+5-6 = 30.62...
Add Comment
Please, Sign In to add comment
Advertisement