Placing towers (and making placeholders) without humanoids in a TD game

Hello everyone, today I was working on my 2nd tower which did not involve humanoids (well it did, but i added extra parts outside of the character. Naturally, my code broke :stuck_out_tongue:
This was gnomecode’s tutorial which was super helpful and ive made a few edits to include a situation with and without humanoids but the issue is im still using humanoidrootpart to set the position. I need to find a way to stop using them so the extra parts outisde of the character will still work. If you dont get it, heres some screenshots:

What it looks like:
image

The hierarchy inside:
image

And then heres my code:
Client (using topbarplus):

		local PS = game:GetService("PhysicsService")
		local RSS = game:GetService("ReplicatedStorage")
		local RunService = game:GetService("RunService")
		local UserInputService = game:GetService("UserInputService")

		local TowerSpawnEvent = RSS.Events.Tower.TowerSpawn
		local towers = RSS:WaitForChild("Towers")
		local screengui = game.Players.LocalPlayer.PlayerGui.TowerGui
		local guifolder = RSS.GUI
		local camera = workspace.CurrentCamera
		local towerTospawn = nil
		local KeybindTowerInfo = nil
		local canPlace = true
		local Cash = game.Players.LocalPlayer.leaderstats.Cash

		local function MouseRaycast(blacklist)
			local mousePosition = UserInputService:GetMouseLocation()
			local mouseRay = camera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
			local raycastParams = RaycastParams.new()
			raycastParams.FilterType = Enum.RaycastFilterType.Exclude
			raycastParams.FilterDescendantsInstances = blacklist
			local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)

			return raycastResult
		end

		local function RemovePlaceholderTower()
			if towerTospawn then
				towerTospawn:Destroy()
				towerTospawn = nil
			end
		end

		local function RemovePlaceholderText()
			if towerTospawn then
				KeybindTowerInfo:Destroy()
				KeybindTowerInfo = nil
			end
		end

		local function AddPlaceholderTower(name)
			local towerExists = towers:FindFirstChild(name)
			if towerExists then
				RemovePlaceholderTower()
				towerTospawn = towerExists:Clone()
				towerTospawn.Parent = workspace.Towers

				for i, object in pairs(towerTospawn:GetDescendants()) do
					if object:IsA("BasePart") then
						object.CollisionGroup = "Tower"
						object.Material = Enum.Material.ForceField
					end
				end
			end
		end

		local function ColorPlaceholderTower(color)
			for i, object in pairs(towerTospawn:GetDescendants()) do
				if object:IsA("BasePart") then
					object.Color = color
				end
			end
		end

		AddPlaceholderTower("dealer")
		KeybindTowerInfo = guifolder.KeybindTowerInfo:Clone()
		KeybindTowerInfo.Parent = screengui

		UserInputService.InputBegan:Connect(function(input, processed)
			if processed then
				return
			end
			if towerTospawn then
				if input.UserInputType == Enum.UserInputType.MouseButton1 then
					if canPlace then
						if Cash.Value >= towerTospawn:GetAttribute("cost") then 
							TowerSpawnEvent:FireServer(towerTospawn.Name, towerTospawn.PrimaryPart.CFrame,true)
							RemovePlaceholderText()
							RemovePlaceholderTower()
						else
							RemovePlaceholderText()
							RemovePlaceholderTower()
						end
					else
						RemovePlaceholderText()
						RemovePlaceholderTower()
					end
				end
				if input.KeyCode == Enum.KeyCode.Q then
					RemovePlaceholderText()
					RemovePlaceholderTower()
				end
			end
		end)

		RunService.RenderStepped:Connect(function()
			if towerTospawn then
				local result = MouseRaycast({towerTospawn})
				if result and result.Instance then
					if result.Instance.Parent.Name == "TowerSpace" then
						canPlace = true
						ColorPlaceholderTower(Color3.new(0,1,0))
					else
						canPlace = false
						ColorPlaceholderTower(Color3.new(1,0,0))
					end
					local x = result.Position.X
					local y = result.Position.Y + 0.6034285714 -- this part used to be the hipheight divided by 1.75 but i changed it to the actual value for now
					local z = result.Position.Z

					local cframe = CFrame.new(x,y,z)
					towerTospawn:SetPrimaryPartCFrame(cframe)
				end
			end
		end)
	end)

Server:

require(script.Parent.Tower)

Module:

local RSS = game:GetService("ReplicatedStorage")
local towers = RSS.Towers
local events = RSS.Events
local towerevents = events.Tower
local tower = {}

local towerspawnevent = towerevents.TowerSpawn

function tower.Spawn(player, name, pos, normal, hasHumanoid)
	if hasHumanoid then
		if normal then
			local towerExists = RSS.Towers:FindFirstChild(name)
			if towerExists then
				player.leaderstats.Cash.Value -= towerExists:GetAttribute("cost")
				local towerToSpawn = towerExists:Clone()
				towerToSpawn.HumanoidRootPart.CFrame = pos
				towerToSpawn.Parent = workspace.Towers
				towerToSpawn.HumanoidRootPart:SetNetworkOwner(nil)

				for i, object in pairs(towerToSpawn:GetDescendants()) do
					if object:IsA("BasePart") then
						object.CollisionGroup = "Tower"
					end
				end
			else
				warn("No Tower Found!")
			end
		else
			local towerExists = RSS.Towers:FindFirstChild(name)
			if towerExists then
				local towerToSpawn = towerExists:Clone()
				towerToSpawn.HumanoidRootPart.CFrame = pos
				towerToSpawn.Parent = workspace.Towers
				towerToSpawn.HumanoidRootPart:SetNetworkOwner(nil)

				for i, object in pairs(towerToSpawn:GetDescendants()) do
					if object:IsA("BasePart") then
						object.CollisionGroup = "Tower"
					end
				end
			else
				warn("No Tower Found!")
			end
		end
	else
		if normal then
			local towerExists = RSS.Towers:FindFirstChild(name)
			if towerExists then
				player.leaderstats.Cash.Value -= towerExists:GetAttribute("cost")
				local towerToSpawn = towerExists:Clone()
				towerToSpawn.HumanoidRootPart.CFrame = pos
				towerToSpawn.Parent = workspace.Towers

				for i, object in pairs(towerToSpawn:GetDescendants()) do
					if object:IsA("BasePart") then
						object.CollisionGroup = "Tower"
					end
				end
			else
				warn("No Tower Found!")
			end
		else
			local towerExists = RSS.Towers:FindFirstChild(name)
			if towerExists then
				local towerToSpawn = towerExists:Clone()
				towerToSpawn.HumanoidRootPart.CFrame = pos
				towerToSpawn.Parent = workspace.Towers

				for i, object in pairs(towerToSpawn:GetDescendants()) do
					if object:IsA("BasePart") then
						object.CollisionGroup = "Tower"
					end
				end
			else
				warn("No Tower Found!")
			end
		end
	end
end

towerspawnevent.OnServerEvent:Connect(tower.Spawn)

return tower
1 Like

try making a separate model w the extra parts nside the character model and welding all of them and one of them with the characterprimarypart