How to make an NPC attack you, when you attack it?

No worries, it can be hard to conceptualize some of that kind of stuff.

I’ve done it quite a lot in many different forms so I get how that can be “weird.”

Biggest advice is do it SIMPLY. Even at the cost of performance starting out. When in doubt or you don’t know how to do it performantly, that’s ok just do it the un performant way to get it working and organize your code knowing your going to make it performant down the line.

2 Likes

Wanted to elaborate more on my idea said before, so I made a mock script of what it might look like. Again, I’m not the best lua coder so take my code more as an outline rather than working code lol

local TW = game:GetService("TweenService")
local MapBounds = {Vector2.new(0,0),Vector2.new(100,100)}
local MaxHeight	= 100
local status = Instance.new("StringValue") -- Replaces the isChasing bool with something that can be used more generally

-- HOW STATUS WORKS:
-- Alternates between 3 values: Aggro (happens when the player screws with the NPC), Idle (happens at the end of movement/at respawn), and Moving (random movement)
-- Uses Changed Connect events to tell when to do each thing

status.Parent = script.Parent -- Feel free to customize this to fit your needs
local plrChar

local track1 = script.Parent.Humanoid.Animator:LoadAnimation(script.WalkAnim)
local track2 = script.Parent.Humanoid.Animator:LoadAnimation(script.IdleAnim)

local function getHeightAtPoint(position : Vector2)
	local Pos3D  = Vector3.new(position.X,MaxHeight,position.Y)
	local result = workspace:Raycast(Pos3D,Vector3.new(0,-1,0)*(MaxHeight*1.1))

	if result and result.Position then
		return result.Position.Y
	end
end

local function getRandomPosition()
	local randX  = math.random(MapBounds[1].X,MapBounds[2].X)
	local randZ  = math.random(MapBounds[1].Y,MapBounds[2].Y)
	local height = getHeightAtPoint(Vector2.new(randX,randZ))

	if not height then error("no height found") end 

	return Vector3.new(randX,height+1,randZ)
end



script.Parent.NPCHit.Event:Connect(function(char)
	plrChar = char
	status = "Aggro"
end)

script.Parent.Humanoid.Died:Connect(function()
	for i, part in pairs(script.Parent:GetChildren()) do
		if part:IsA("BasePart") then
			TW:Create(part, TweenInfo.new(0.7), {Transparency=1}):Play()
		end
	end
	task.wait(.7) -- Should probably change to a wait for child
	script.Parent.Parent = game.ReplicatedStorage
	status = "Idle"
end)

local function explore() -- Fell free to rename these crappy names. This is the "Moving" part of the script
	status = "Moving"
	track2:Stop()
	track1:Play()
	script.Parent.Humanoid:MoveTo(getRandomPosition())
	script.Parent.Humanoid.MoveToFinished:Wait()
	status = "Idle"
end

local function searchAndDestroy() -- AKA "isChasing". Again, sorry with the bad name-- I'm creatively bankrupt rn
	track1:Stop()
	track2:Play()
	repeat
		script.Parent.Humanoid:MoveTo(plrChar.HumanoidRootPart.Position)
		track2:Stop()
		track1:Play() 
		script.Parent.Humanoid.MoveToFinished:Wait()
	until script.Parent.HumanoidRootPart.Position - plrChar.HumanoidRootPart.Position.Magnitude < 30
	status = "Idle"
end

status.Changed:Connect(function(newValue)
	if newValue == "Idle" then
		explore()
	elseif newValue == "Aggro" then
		searchAndDestroy()
	end
	-- No Moving check because nothing's gonna happen if it sees its moving. Mainly there for conditioning checks and whatnot
end)
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.