Attack that's supposed to spawn a surprise attack onto every Player and NPC in radius keeps spawning attack on me instead

As the title suggests, I’m trying to make like this move for a fighting game I’m working on that spawns an attack (Be it a circle of attacks you need to dodge or one attack that spawns behind you) on top of all Players and NPCs in radius.

While the attack itself does spawn in (at least before I added a line of code that prevents it from targetting yourself, which I have not tested yet), it spawns on me instead and nothing else.
Here is the code that I have for the Tool so far:

active = 1
ogName = script.Parent.Name
script.Parent.Activated:Connect(function()
	if active == 1 then
		active = 0
		script.Parent.owner.Value = script.Parent.Parent --Sets the value that prevents the attack from locking onto yourself later on in the code
		for i, v in next, game.Players:GetPlayers() do
			if not v.Character then continue end
			if (v.Character.HumanoidRootPart.Position - script.Parent.Parent.HumanoidRootPart.Position).magnitude < 500 and v.Character.Name ~= script.Parent.owner.Value then --If targets are within 500 studs and they aren't yourself
				local g = game.ReplicatedStorage["Crazed Sans"].Circle:Clone()
				g.owner.Value = script.Parent.Parent
				g.Parent = workspace
				g.PrimaryPart = g.Head
				g:SetPrimaryPartCFrame(v.Character["HumanoidRootPart"].CFrame * CFrame.new(0,0,0)) --Spawns the attack on top of the player
			end
		end
		local cd = script.Parent.cd.Value
		local origcd = cd
		for i = 1,cd do
			script.Parent.Name = cd/(script.Parent.division.Value)
			task.wait(1/(script.Parent.division.Value))
			cd-=1
		end
		cd = origcd
		script.Parent.Name = ogName
		active = 1
	end
end)

Now I do have a feeling that within the script I shouldn’t have just the Players being called upon if I want it to target NPCs with Humanoid properties too, though I’m not quite sure how I would go about doing that since NPCs aren’t classified as players. I mostly want it to target NPCs too for testing purposes, just to make sure it works properly because I don’t really have any testers for this game.

As stated before I have tried making it so that the attack doesn’t spawn itself onto yourself, have the owner value be set to the user’s name so the script will skip over the user when spawning the attack onto other people, but it did still end up spawning the attack onto me. It might be because the game is looking for any other player to use it on but couldn’t find any so it defaulted to just spawning it on me?

Any and all help will be greatly appreciated, thank you ! ^ ^

What is script.Parent.owner exactly?
There’s no reason to use a ValueBase object here anyway since you can just store it in a variable which will be more reliable.

You seem to be comparing a string v.Character.Name to a Player|nil script.Parent.owner.Value. If owner is an ObjectValue, you need to compare like types, either by getting the Name field or by changing v.Character.Name to something else.

1 Like

Honestly I can barely read this, add some print statements and look at the workspace to see if anything is actually happening.

For npcs instead of only checking the getplayer table I would use a hitbox that you put on the player for a split second and then sort everything through a .Touched event and only take characters with humanoids.

https://create.roblox.com/docs/reference/engine/classes/BasePart#Touched

To make your script more readable for debugging and easier to get feedback from, use full names for variables and use the actual built in boolean true/false instead of 0 or 1. Also theres a ton of redundant code in this segment, stop using so many object values instead of variables.

1 Like

script.Parent.owner in this case is an ObjectValue
Perhaps I should take out the Name part of the v.Character.Name part of the code? Because it’ll fetch the player itself rather than the name which is a string

I’ve never really worked with .Touched events before, the most experience I have with them are with RemoteEvents that I use to make zone attacks function (attacks where you click somewhere on the map and it’ll create an attack there)

Also variables? Are you talking about the stuff in the script that’s like “active = 1”?

Ok update; removing the Name part from that bit of code did stop the attack from spawning on myself so thank you for that ^ ^

.Touched events are pretty easy to implement if you look at the documentation. It will just basically fire if any part touches the part you put the event on.

A variable is just anything you set a value to like
X = 1
Value = “String”

1 Like

Ohh gotcha, I will be looking into that real quick :+1:

By the way I have a lot of Value reliance in this script (namely in the cooldown area) because years ago I did have the cooldowns set as a variable, but I figured if I made them values inside the tool instead it would save me some time of manually going inside of the script and changing it from there

Wait real quick do .Touched events require like a RemoveEvent? Because every attack in this game I’m working on uses “function OnTouched(Part)” and “script.Parent.Touched:connect(OnTouched)” inside of their hurt scripts but they don’t have any RemoveEvents in them yet they work just fine?

No they don’t need remoteevents. Remotevents are just for sending information between server and client.

1 Like

Attribute values would definitely be useful for this if you look at the documentation.

1 Like

Ok so I tried your approach, so far it has worked really well but I do fear it might not be fully reliable though because I did playtest it, after a bit of nonstop spamming the attack (1 second cooldown) the spawner will sometimes fail to tag some or sometimes all the NPCs caught within the 300 stud cylinder used to spawn in the spears on everything inside of it

I am still using the “function OnTouched(Part)” and “script.Parent.Touched:connect(OnTouched)” method of gathering contact with the cylinder itself, it is working so far but it is having some hiccups with it sometimes not registering some of the NPCs within the cylinder as within range

Thats been known to happen with .Touched. Fortunately theres an alternative which is a bit more complex but fixes the problem.

https://create.roblox.com/docs/reference/engine/classes/WorldRoot#GetPartBoundsInBox

Basically the same thing as .Touched except its a simulated box instead of an actual part. In you’re code you could just replace the code for the hitbox you’re using with something like

local CFrame = Character.Head
local size = Vector3.new(300, 300, 300) -- note this would create a box instead of a cylinder
local params -- you could probably just ignore this atleast for now, as its for filtering what parts you wanna add or not add to the array and its a bit complex.

local PartsArray = WorldRoot:GetPartBoundsInBox(CFrame, size, params) -- this returns an array of parts that was touched which you could then sort with a for loop```
1 Like

.Touched can easily fail because it needs movement to happen in order to trigger, even more so if the part with the .Touched connection itself is anchored because in that case it relies entirely on the other part to move. .Touched from my experience is bad practice for a hitbox and I would rather go with the suggestion that @freezekill123 made.

1 Like

Ok yeah this is a lot more complicated

First of all I’m not even sure where to factor this into, do I put this in the original cylinder thing? Do I put it in the Tool to generate an entirely new hitbox?

Even then, I wouldn’t really know how to get the box to like trigger the attack spawn, I looked at the document page on it and it unfortunately didn’t really help me out much

But I do really want to try this out, to see if it’ll perform better than the Touched event method I was using earlier

GetPartsBounds() basically does it automatically, you don’t need to manually create a part.

WorldRoot:GetPartBoundsInBox(cframe:CFrame, size:Vector3, overlapParams:OverlapParams)
cframe: The position of the center of the box (if you want it to be at 0,100,0 you use CFrame.new(0,100,0) for example).

size: The size of the box that you want to look for parts in.

OverlapParams: You can create a variable for it like local Params = OverlapParams.new() and then you can use that variable to see what kind of uses it has, it can filter Instances out of the function for example.

Hope this helps explaining how the function works

2 Likes

To add on to what the other guy said. You would just put all the code you had spread over the other scripts all into the tool script and get rid of the cylinder.

And you would effectively filter it by using a for loop over the PartsArray so

for i, part in ipairs(PartsArray) do
    if part.parent:findfirstchild("Humanoid") then -- if the character model has a humanoid and if it does then it means its a player or npc
       return true -- replace this with whatever code you used to actually activate the ability
    end
end
2 Likes

Ok so the first problem with this, whenever I put in the WorldRoot part of the code it’ll determine “WorldRoot” as an unknown global

Second, whenever I leave the params variable empty like in freezekill123’s example code, it’ll error param in the WorldRoot part of the code saying it has no variable assigned, so I changed the initial variable to be nil to hopefully fix that error

And finally I have no idea where I’m supposed to go from there, I don’t even know how to fix the WorldRoot unknown global error so I wouldn’t really know what to do with freezekill123’s next example code

  1. Instead of WorldRoot I think you want to use workspace:GetPartBoundsInBox
  2. Could you show a code snippet where you used this?
  3. The function returns an array of parts that were detected, so you could scan through them and find all the player’s characters
1 Like

Alright, i got it to work !
I changed the WorldRoot to workspace, made a few altercations to the code and now it works perfectly !

Thank you so much to you and freezekill123 for your help ! ^ ^