Hi im creating a Tower Defense game but upgrade system is glitched how i can fix?
error:
Tower module script:
local PhysicsService = game:GetService("PhysicsService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local functions = ReplicatedStorage:WaitForChild("Functions")
local requestTowerFunction = functions:WaitForChild("RequestTower")
local sellTowerFunction = functions:WaitForChild("SellTower")
local spawnTowerFunction = functions:WaitForChild("SpawnTower")
local changeModeFunction = functions:WaitForChild("ChangeTowerMode")
local map = workspace.Grassland
local Events = ReplicatedStorage:WaitForChild("Events")
local animateTowerEvent = Events:WaitForChild("AnimateTower")
local maxTowers = 10
local tower = {}
function tower.FindTarget(newTower, range, mode)
local bestTarget = nil
local bestWaypoint = nil
local bestDistance = nil
local bestHealth = nil
for i, mob in ipairs(workspace.Mobs:GetChildren()) do
local distanceToMob = (mob.HumanoidRootPart.Position - newTower.HumanoidRootPart.Position).Magnitude
local distanceToWaypoint = (mob.HumanoidRootPart.Position - map.Waypoints[mob.MovingTo.Value].Position).Magnitude
if distanceToMob <= range then
if mode == "Near" then
range = distanceToMob
bestTarget = mob
elseif mode == "First" then
if not bestWaypoint or mob.MovingTo.Value >= bestWaypoint then
bestWaypoint = mob.MovingTo.Value
if not bestDistance or distanceToWaypoint < bestDistance then
bestDistance = distanceToWaypoint
bestTarget = mob
end
end
elseif mode == "Last" then
if not bestWaypoint or mob.MovingTo.Value <= bestWaypoint then
bestWaypoint = mob.MovingTo.Value
if not bestDistance or distanceToWaypoint > bestDistance then
bestDistance = distanceToWaypoint
bestTarget = mob
end
end
elseif mode == "Strong" then
if not bestHealth or mob.Humanoid.Health > bestHealth then
bestHealth = mob.Humanoid.Health
bestTarget = mob
end
elseif mode == "Weak" then
if not bestHealth or mob.Humanoid.Health < bestHealth then
bestHealth = mob.Humanoid.Health
bestTarget = mob
end
end
end
end
return bestTarget
end
function tower.Attack(newTower, player)
local config = newTower.Config
local target = tower.FindTarget(newTower, config.Range.Value, config.TargetMode.Value)
if target and target:FindFirstChild("Humanoid") and target.Humanoid.Health > 0 then
local targetCFrame = CFrame.lookAt(newTower.HumanoidRootPart.Position, target.HumanoidRootPart.Position)
newTower.HumanoidRootPart.BodyGyro.CFrame = targetCFrame
animateTowerEvent:FireAllClients(newTower, "Attack")
target.Humanoid:TakeDamage(config.Damage.Value)
if target.Humanoid.Health <= 0 then
player.Gold.Value += target.Humanoid.MaxHealth
end
task.wait(config.Cooldown.Value)
end
task.wait(0.1)
if newTower and newTower.Parent then
tower.Attack(newTower, player)
end
end
function tower.ChangeMode(player, model)
if model and model:FindFirstChild("Config") then
local targetMode = model.Config.TargetMode
local modes = {"First", "Last", "Near", "Strong", "Weak"}
local modeIndex = table.find(modes, targetMode.Value)
if modeIndex < #modes then
targetMode.Value = modes[modeIndex + 1]
else
targetMode.Value = modes[1]
end
return true
else
warn("Unable to change tower mode")
return false
end
end
changeModeFunction.OnServerInvoke = tower.ChangeMode
function tower.Sell(player, model)
if model and model:FindFirstChild("Config") then
if model and model:FindFirstChild("Config") then
player.PlacedTowers.Value -= 1
player.Gold.Value += model.Config.Price.Value
model:Destroy()
return true
end
end
warn("Unable to sell this tower")
return false
end
sellTowerFunction.OnServerInvoke = tower.Sell
function tower.Spawn(player, name, cframe, previous)
local allowedToSpawn = tower.CheckSpawn(player, name)
local oldMode = nil
if allowedToSpawn then
local newTower
if previous then
oldMode = previous:WaitForChild("Config").TargetMode.Value
previous:Destroy()
newTower = ReplicatedStorage:WaitForChild("Towers").Upgrades[name]:Clone()
else
newTower = ReplicatedStorage.Towers[name]:Clone()
player.PlacedTowers.Value += 1
end
local ownerValue = Instance.new("StringValue")
ownerValue.Name = "Owner"
ownerValue.Value = player.Name
ownerValue.Parent = newTower.Config
local targetMode = Instance.new("StringValue")
targetMode.Name = "TargetMode"
targetMode.Value = oldMode or "First"
targetMode.Parent = newTower.Config
newTower.HumanoidRootPart.CFrame = cframe
newTower.Parent = workspace.Towers
newTower.HumanoidRootPart:SetNetworkOwner(nil)
local bodyGyro = Instance.new("BodyGyro")
bodyGyro.MaxTorque = Vector3.new(math.huge, math.huge, math.huge)
bodyGyro.D = 0
bodyGyro.CFrame = newTower.HumanoidRootPart.CFrame
bodyGyro.Parent = newTower.HumanoidRootPart
for i, object in ipairs(newTower:GetDescendants()) do
if object:isA("BasePart") then
PhysicsService:SetPartCollisionGroup(object, "Tower")
end
end
player.Gold.Value -= newTower.Config.Price.Value
coroutine.wrap(tower.Attack)(newTower, player)
return newTower
else
warn("Requested tower does not exist:", name)
return false
end
end
spawnTowerFunction.OnServerInvoke = tower.Spawn
function tower.CheckSpawn(player, name, previous)
local towerExists = ReplicatedStorage.Towers:FindFirstChild(name, true)
if towerExists then
if towerExists.Config.Price.Value <= player.Gold.Value then
if previous or player.PlacedTowers.Value < maxTowers then
return true
else
warn("Player was reached max limit")
end
else
warn("Player cannot afford")
end
else
warn("That tower does not exist")
end
return false
end
requestTowerFunction.OnServerInvoke = tower.CheckSpawn
return tower
Localscript:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local PhysicsService = game:GetService("PhysicsService")
local Modules = ReplicatedStorage:WaitForChild("Modules")
local health = require(Modules:WaitForChild("Health"))
local towers = ReplicatedStorage:WaitForChild("Towers")
local functions = ReplicatedStorage:WaitForChild("Functions")
local requestTowerFunction = functions:WaitForChild("RequestTower")
local spawnTowerFunction = functions:WaitForChild("SpawnTower")
local sellTowerFunction = functions:WaitForChild("SellTower")
local changeModeFunction = functions:WaitForChild("ChangeTowerMode")
local gold = Players.LocalPlayer:WaitForChild("Gold")
local map = workspace:WaitForChild("Grassland")
local base = map:WaitForChild("Base")
local info = workspace:WaitForChild("Info")
local camera = workspace.CurrentCamera
local gui = script.Parent
local lastTouch = tick()
local canPlace = false
local towerToSpawn = nil
local hoveredInstance = nil
local selectedTower = nil
local Rotation = 0
local placedTowers = 0
local maxTowers = 10
local function SetupGui()
health.Setup(base, gui.Info.Health)
workspace.Mobs.ChildAdded:Connect(function(mob)
health.Setup(mob)
end)
info.Message.Changed:Connect(function(change)
gui.Info.Message.Text = change
if change == "" then
gui.Info.Message.Visible = false
else
gui.Info.Message.Visible = true
end
end)
info.Wave.Changed:Connect(function(change)
gui.Info.Stats.Wave.Text = "Wave:" .. change
end)
gold.Changed:Connect(function(change)
gui.Info.Stats.Gold.Text = "$" .. gold.Value
end)
gui.Info.Stats.Gold.Text = "$" .. gold.Value
end
SetupGui()
local function MouseRaycast(model)
local mousePosition = UserInputService:GetMouseLocation()
local mouseRay = camera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
local raycastParams = RaycastParams.new()
local blacklist = camera:GetChildren()
table.insert(blacklist, model)
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
raycastParams.FilterDescendantsInstances = blacklist
local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)
return raycastResult
end
local function CreateRangeCircle(tower, placeholder)
local range = tower.Config.Range.Value
local height = (tower.PrimaryPart.Size.Y / 2) + tower.Humanoid.HipHeight
local offset = CFrame.new( 0, -height, 0)
local p = Instance.new("Part")
p.Name = "Range"
p.Shape = Enum.PartType.Cylinder
p.Material = Enum.Material.Neon
p.Transparency = 0.9
p.Size = Vector3.new(2, range * 2, range * 2)
p.TopSurface = Enum.SurfaceType.Smooth
p.BottomSurface = Enum.SurfaceType.Smooth
p.CFrame = tower.PrimaryPart.CFrame * offset * CFrame.Angles(0, 0, math.rad(90))
p.CanCollide = false
p.Anchored = true
p.Parent = workspace.Camera
if placeholder then
p.Anchored = false
local weld = Instance.new("WeldConstraint")
weld.Part0 = p
weld.Part1 = tower.PrimaryPart
weld.Parent = p
p.Parent = tower
else
p.Anchored = true
p.Parent = workspace.Camera
end
end
local function RemovePlaceholderTower()
if towerToSpawn then
towerToSpawn:Destroy()
towerToSpawn = nil
Rotation = 0
gui.Controls.Visible = false
end
end
local function AddPlaceholderTower(name)
local towerExists = towers:FindFirstChild(name)
if towerExists then
RemovePlaceholderTower()
towerToSpawn = towerExists:Clone()
towerToSpawn.Parent = workspace
CreateRangeCircle(towerToSpawn, true)
for i, object in ipairs(towerToSpawn:GetDescendants()) do
if object:isA("BasePart") then
PhysicsService:SetPartCollisionGroup(object, "Tower")
if object.Name ~= "Range" then
object.Material = Enum.Material.ForceField
object.Transparency = 0.3
end
end
end
gui.Controls.Visible = true
end
end
local function ColorPlaceholderTower(color)
for i, object in ipairs(towerToSpawn:GetDescendants()) do
if object:isA("BasePart") then
object.Color = color
end
end
end
local function toggleTowerInfo()
workspace.Camera:ClearAllChildren()
gui.Towers.Title.Text = "Towers: " .. placedTowers .. "/" .. maxTowers
if selectedTower then
CreateRangeCircle(selectedTower)
gui.Selection.Visible = true
local config = selectedTower.Config
gui.Selection.Stats.Damage.Value.Text = config.Damage.Value
gui.Selection.Stats.Range.Value.Text = config.Range.Value
gui.Selection.Stats.Cooldown.Value.Text = config.Cooldown.Value
gui.Selection.Title.TowerName.Text = selectedTower.Name
gui.Selection.Title.TowerImage.Image = config.Image.Texture
gui.Selection.Title.OwnerName.Text = config.Owner.Value .. "'s"
local modes = {
["First"] = "rgb(150, 150, 150)",
["Last"] = "rgb(50, 50, 50)",
["Near"] = "rgb(50, 150, 0)",
["Strong"] = "rgb(200, 50, 50)",
["Weak"] = "rgb(50, 100, 200)"
}
local color = modes[config.TargetMode.Value]
gui.Selection.Action.Target.Title.Text = "Target: <font color=\"" .. color .. "\">" .. config.TargetMode.Value .. "</font>"
if config.Owner.Value == Players.LocalPlayer.Name then
gui.Selection.Action.Visible = true
local upgradeTower = config:FindFirstChild("Upgrade")
if upgradeTower then
gui.Selection.Action.Upgrade.Visible = true
gui.Selection.Action.Upgrade.Title.Text = "Upgrade (" ..upgradeTower.Value.Config.Price.Value .. ")"
else
gui.Selection.Action.Upgrade.Visible = false
end
else
gui.Selection.Action.Visible = false
end
else
gui.Selection.Visible = false
end
end
local function SpawnNewTower()
if canPlace then
local placedTower = spawnTowerFunction:InvokeServer(towerToSpawn.Name, towerToSpawn.PrimaryPart.CFrame)
if placedTower then
placedTowers +=1
selectedTower = placedTower
RemovePlaceholderTower()
toggleTowerInfo()
end
end
end
gui.Controls.Cancel.Activated:Connect(RemovePlaceholderTower)
gui.Selection.Action.Target.Activated:Connect(function()
if selectedTower then
local modeChangeSuccess = changeModeFunction:InvokeServer(selectedTower)
if modeChangeSuccess then
toggleTowerInfo()
end
end
end)
gui.Selection.Action.Sell.Activated:Connect(function()
if selectedTower then
local soldTower = sellTowerFunction:InvokeServer(selectedTower)
if soldTower then
selectedTower = nil
placedTowers -= 1
toggleTowerInfo()
end
end
end)
gui.Selection.Action.Upgrade.Activated:Connect(function()
if selectedTower then
local upgradeTower = selectedTower.Config.Upgrade.Value
local upgradeSuccess = spawnTowerFunction:InvokeServer(upgradeTower.Name, selectedTower.PrimaryPart.CFrame, selectedTower)
if upgradeSuccess then
selectedTower = upgradeSuccess
toggleTowerInfo()
end
end
end)
UserInputService.InputBegan:Connect(function(input, processed)
if processed then
return
end
if towerToSpawn then
if input.UserInputType == Enum.UserInputType.MouseButton1 then
SpawnNewTower()
elseif input.UserInputType == Enum.UserInputType.Touch then
local timeSinceLastTouch = tick() - lastTouch
if timeSinceLastTouch <= 0.25 then
SpawnNewTower()
end
lastTouch = tick()
elseif input.KeyCode == Enum.KeyCode.R then
Rotation += 45
elseif input.KeyCode == Enum.KeyCode.X or input.KeyCode == Enum.KeyCode.C then
RemovePlaceholderTower()
end
elseif hoveredInstance and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then
local model = hoveredInstance:FindFirstAncestorOfClass("Model")
if model and model.Parent == workspace.Towers then
selectedTower = model
else
selectedTower = nil
end
toggleTowerInfo()
end
end)
gui.Towers.Title.Text = "Towers:" .. placedTowers .. "/" .. maxTowers
for i, tower in pairs(towers:GetChildren()) do
if tower:IsA("Model") then
local button = gui.Towers.Template:Clone()
local config = tower:WaitForChild("Config")
button.Name = tower.Name
button.Image= config.Image.Texture
button.Visible = true
button.Parent = gui.Towers
button.Price.Text = config.Price.Value
button.LayoutOrder = config.Price.Value
button.Activated:Connect(function()
local allowedToSpawn = requestTowerFunction:InvokeServer(tower.Name)
if allowedToSpawn then
AddPlaceholderTower(tower.Name)
end
end)
end
end
RunService.RenderStepped:Connect(function()
local result = MouseRaycast(towerToSpawn)
if result and result.Instance then
if towerToSpawn then
hoveredInstance = nil
if result.Instance.Parent.Name == "TowerArea" 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 + towerToSpawn.Humanoid.HipHeight + (towerToSpawn.PrimaryPart.Size.Y / 2)
local z = result.Position.Z
local cframe = CFrame.new(x, y, z) * CFrame.Angles(0, math.rad(Rotation), 0)
towerToSpawn:SetPrimaryPartCFrame(cframe)
else
hoveredInstance = result.Instance
end
else
hoveredInstance = nil
end
end)