Need help with Acid-Sprayer

So i was coding some acid (that would get emited from broken pipelines) But im not quite sure how to go about creating little puddles on the ground. (that dont hurt)
So far ive tried coding it myself and finding things here (on the devforums).

My code:
–Emitter code (parent is the part to emit from)

local serverstor = game.ServerStorage
local SIGNALS = serverstor.SIGNALS
local event = SIGNALS.DropletHit
local m = math

local fall = serverstor.STORAGE.Droplets.Fall
local puddle = serverstor.STORAGE.Droplets.Puddle

event.Event:Connect(function(X, Z, Part)
	if Part.ClassName == "Part" then
		--baseplate
		local clone = puddle:Clone()
		local Y = Part.Size.Y + Part.Position.Y
		clone.Position = Vector3.new(X, Y, Z)
	else
		--player
		local hum = Part.Character:FindFirstChildOfClass("Humanoid")
		if hum then
			if hum.Health - 1 < 1 then
				SIGNALS.Player_Died:Fire(Part, "WS")
			else
				hum.Health -= 1
			end
		end
	end
end)

local rate = 10        --per second
local startingsize = 2 -- in studs

while true do
	wait(1/rate)
	local c = fall:Clone()
	c.Droplet_1.Enabled = true
	local de = script.Parent.Position
	local si = script.Parent.Size
	local q = (de.X + m.random(-si.X/2, si.X/2)+0.01)
	local w = (de.Y + m.random(-si.Y/2, si.Y/2)+0.01)
	local e = (de.Z + m.random(-si.Z/2, si.Z/2)+0.01)
	c.Position = Vector3.new(q, w, e)
	c.Parent = workspace.Visuals.Droplets
end

this is the way its stored for clarification.
devforums

Droplet_1 code:

local droplet = script.Parent
local droplets = workspace.Visuals.Droplets
local S_puddle = workspace.Visuals.SmallPuddles

local Signal = game.ServerStorage.SIGNALS.DropletHit
droplet.Touched:Connect(function(hit)
	local tmp = game.Players:GetPlayerFromCharacter(hit.Parent)
	if hit.Name == "Baseplate" then
		
		Signal:Fire(droplet.Position.X, droplet.Position.Z, hit)
		droplet:Destroy()
	elseif tmp then
		
		Signal:Fire(droplet.Position.X, droplet.Position.Z, tmp)
		droplet:Destroy()
	elseif hit.Parent == S_puddle then
		local tmpval = hit:FindFirstChild("size")
		if tmpval then
			tmpval.Value += 1.5
		end
		droplet:Destroy()
	end
	
end)
wait(10)
droplet:Destroy()

droplet_2 code:

local part = script.Parent
local sizeval = part:FindFirstChildOfClass("NumberValue")
local Max = math.max()

sizeval.Changed:Connect(function()
	part.Size = Vector3.new(Max(0.005*sizeval.Value, 0.005), sizeval.Value, sizeval.Value)
end)

while true do
	wait(0.02)
	sizeval.Value -= 0.01
	if sizeval.Value < 0.05 then
		part:Destroy()
	end
end

So it succesfully creates the droplets, But it doesnt make the puddles however it still deals damage to the player, I would need another way to interact with the baseplate other then “touched” event but i have no clue.

2 Likes

Recommend using bloodengine for this and rewriting it a bit to suit your needs

to complicated and Im trying to make this as compact as possible, Blood engine is WAY to fancy and needish

edit: and like i SAID CUZ YOU DIDNT READ IT I already searched things up! No need to repeat it!

First off, relax please. The explanation of what you’re asking for was a bit short, so people ask questions to better understand the problem or whatever is being asked about. There were little to no explanations and text surrounding what you were trying to achieve compared to all the code being sent, so he, nor me, really understand if there is a problem with the code (i.e. it’s not doing what it’s supposed to be doing), if you don’t know how to make what you’re asking for, or you’re asking how to improve it.

3 Likes

sorry I guess, I was at that time really stressed since im working on a deadline…
Ive already managed to work on something else for the time being.

And ill edit my post to explain whats going on.

I believe the reason it’s not creating puddles, is that you never set the Disabled property of Droplet_2 to false when cloning the puddle in server storage. You also never set it’s parent to workspace, so it wont be visible either.

There are a handful of ways to interact with the baseplate without the .Touched event, but I’d suggest you use raycasts. So you’d make the droplet not collidable (CanCollide = false) and every frame (RunService.Heartbeat) raycast from the droplet’s last position to the current position. If the ray intersects the baseplate, or really any collider that droplets should collide with, you set the intersection point (RaycastResult.Position) as the position for the puddle.

I’d also like to point out, that the droplet_2 code is broken. First off, when you define Max as math.max(), the round brackets there mean, that you’re defining Max as the result of the function math.max() running, not the function itself. To define a function as a variable (which you’re trying to do), you want to remove the round brackets. If you’ve noticed, that’s also the way that functions are passed into other functions (also known as callbacks) like here on the last line (Tool | Documentation - Roblox Creator Hub)

local tool = script.Parent

local function explode(point)
	-- Explosion code. Not important
end

local function onActivated()
	-- Get the Humanoid that Activated the tool
	local human = tool.Parent.Humanoid
	-- Call explode with the current point the Humanoid is targetting
	explode(human.TargetPoint)
end

tool.Activated:Connect(onActivated) -- Function is passed *without* the round brackets

Now, I’d say that this part is also useless

sizeval.Changed:Connect(function()
	part.Size = Vector3.new(Max(0.005*sizeval.Value, 0.005), sizeval.Value, sizeval.Value)
end)

Because you could just do that inside the while loop after setting sizeval.Value.

Now onto the while loop, First, wait() is border-line deprecated, because when called repeatedly, mostly with small wait times, it doesn’t actually wait even close to the amount of time it was supposed to wait, use task.wait() instead. Second, don’t use either here. In case you don’t know, wait(n) (and task.wait(n)) basically work like this: Every frame after the function was called, until the waiting stops, the function checks if the amount of time passed from when the function was called to now is more than n. If so, the waiting stops. Now that means, that it doesn’t ever wait exactly n seconds, but rather a bit more (which in your case of wait(0.02) would make the waiting time about 1.5x to 2x longer, or even more, depending on the frame rate).
Instead, you’d want to use RunService.Heartbeat and deltaTime that it gives as a parameter.
So it would come out to something like this

local RunService = game:GetService("RunService")

local part = script.Parent
local initialSize = 2
local ySize = 0.005
local minSize = 0.05 -- If the puddle gets smaller than this, it gets destroyed

--
local shrinkSpeed = 1 -- studs per second

-- We don't even need sizeval, as we can just make a variable for it,
-- unless you want the size to be accessible for other scripts, but there'd
-- be better methods for that too
local size = initialSize

-- If you don't know, RunService.Heartbeat is an event that fires every
-- frame and deltaTime is the time from the last frame to the current one
RunService.Heartbeat:Connect(function(deltaTime)
    -- deltaTime is also basically the amount of time we waited
    
    -- Just calculates how much we should shrink the puddle
    -- depending on the amount of time passed
    size -= shrinkSpeed * deltaTime 
   
    
    if size < minSize then
        part:Destroy()
    else
        part.Size = Vector3.new(size, ySize, size)
    end
end)

You could also add easing functions to the puddle shrinking, such as the ones that you can use with TweenSevice, or you could also just tween the size if you want to.

1 Like

I see… Thanks for highlighting some things i forgot.

but is still want the puddles to use a variable, so that i can change it. However you said that there are other ways for this?

I’ve also followed your advice on how to use heartbeat instead of a wait() loop.
But I dont understand if its like more effiecient or something like that, my game is supposed to be old-fashioned. Optimization is great but I don’t think it would make that huge of an impact since I’ll only use this like ~30 times.

ill mark yours as the solution since it fixes all the things.

The thing with wait, as I mentioned before, is that it gets very inaccurate, mostly at lower wait times. So the puddle would disappear at the wrong speed, and would even be affected by frame rate.

1 Like

I have unmarked it as a solution since with the raycasting and then using tween it does not work, Canyou please script it for me im so tired of that this shit for 1 thing thats supposed to be a DETAIL and not something i have to spend 30 FRICKING HOURS ON.
I am going insane by how hard such a simple thing actually is…
The code i tried:

--droplet_1
local droplet = script.Parent
local droplets = workspace.Visuals.Droplets
local puddles = workspace.Visuals.SmallPuddles
local S_tween = game:GetService("TweenService")
local Signal = game.ServerStorage.SIGNALS.DropletHit

local Pos = droplet.Position
local dir = Vector3.new(-90,0,0)
local RaycastParam = RaycastParams.new()
RaycastParam.IgnoreWater = true
RaycastParam.FilterType = Enum.RaycastFilterType.Exclude

local Cast = workspace:Raycast(Pos, dir, RaycastParam)
wait(0.5)
if Cast then
	local interPos = Cast.Position
	local interPart = Cast.Instance
	local tmp = game.Players:GetPlayerFromCharacter(interPart.Parent)
	local sign = 0
	
	if tmp then
		sign = 1
	elseif interPart.Name == "Baseplate" then
		sign = 2
	elseif interPart.Parent == puddles then
		local sizer = interPart:FindFirstChildOfClass("NumberValue")
		if sizer then
			local raw = sizer.Value + 0.5 - sizer.Value/50
			sizer.Value = math.round(raw*100)/100
		end
	end
	--do the tween after everything math related
	local info = TweenInfo.new(
		0.5,
		Enum.EasingStyle.Linear,
		Enum.EasingDirection.In,
		0,
		false,
		0
	)
	local targetpos = Vector3.new(Pos.X, 0, Pos.Z)
	local new = S_tween:Create(droplet, info, {Position = targetpos})
	new:Play()
	
	wait(new.Completed)
	
	
	--send the signals for the puddle after tween is done.
	if sign > 0 then
		if sign == 1 then
			Signal:Fire(tmp, nil, nil)
		else
			Signal:Fire(interPos.X, interPos.Z, hit)
		end
	else
		print("Made puddle grow and thus no signal can be sent.")
	end
else
	warn("Cast was Infinite or wasnt cast yet")
end
droplet:Destroy()

It keeps saying the raycast failed and i have no idea.

1 Like

first, use task.wait() over wait() as it’s safer,
second: in your above’s script i saw that droplet falls, it might be in wrong position when raycast is done

The wait doesnt matter? its to be sure that the raycast is done, and i only putter it in after it failed and was trying to debug it.

The wrong position? Its using the position its in at that moment.

1 Like

I’m supposing that the droplet is falling. First off, why are you raycasting on the X axis, aka to the right not down? Secondly, I meant it in a way, that every frame (RunService.Heartbeat), you raycast from the last position of the droplet to the new one. Also, make sure the droplet’s CanCollide is off for this. So it would look something like this:

local lastPosition = droplet.Position
RunService.Heartbeat:Connect(function()
    local curPosition = droplet.Position
    local direction = curPosition - lastPosition
    local result = workspace:Raycast(lastPosition, direction, RaycastParams)

    if not result or not result.Position then -- The raycast didn't hit anything
        return
    end

    local hitPosition= result.Position -- this is where the raycast hit. You could create a puddle here and destroy the droplet now.
    -- Make sure that this function would stop running once it reaches here, for example by destroying the droplet
end)
2 Likes

still, use task.wait as it’s more performant

still, i made basic code that might work, you can try to implement it inside your

function Acid:CreateDroplets(startPosition:Vector3,droplets:number)
  local droplets = {}
  for i = 1, droplets do
    local result = workspace:Raycast(start,Vector3.new(0,-90,0))
    if result then
      local dropletPosition = result.Position
      -- code to place droplets on the floor and stores it inside table
    end
  end
  
  return droplets
end

function Acid:CreatePuddle(startPosition:Vector3,Droplets:{})
  local sum = 0
  for droplet, size in Droplets do
    sum += size
  end
  
  if sum >= (Your puddle min size) then
    -- creates new puddle with size of sum or other thing
    return puddle
  end
end

After those 2 functions it would be simplier to add touched event or smth to detect if player actually get damaged or not