|
|
|
|
| local Players = game:GetService("Players")
|
| local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
| local CollectionService = game:GetService("CollectionService")
|
|
|
| local ChoppingConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("ChoppingConfig"))
|
| local CraftingConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("CraftingConfig"))
|
| local ChopEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("ChopEvent")
|
| local NotificationEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("NotificationEvent")
|
| local SoundEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("SoundEvent")
|
|
|
| local function getEquippedAxeOrFist(player)
|
| local axeType = player:GetAttribute("EquippedAxe")
|
|
|
|
|
| if not axeType or axeType == "" or axeType == "None" then
|
| return CraftingConfig.Fist, "Fist"
|
| end
|
|
|
| local axeStats = ChoppingConfig.AxeTypes[axeType]
|
| if axeStats then
|
| return axeStats, axeType
|
| end
|
|
|
|
|
| return CraftingConfig.Fist, "Fist"
|
| end
|
|
|
| local function createChopParticles(position, color)
|
| local attachment = Instance.new("Attachment")
|
| attachment.WorldPosition = position
|
| attachment.Parent = workspace.Terrain
|
|
|
| local emitter = Instance.new("ParticleEmitter")
|
| emitter.Color = ColorSequence.new(color or Color3.fromRGB(180, 140, 100))
|
| emitter.Size = NumberSequence.new({
|
| NumberSequenceKeypoint.new(0, 0.5),
|
| NumberSequenceKeypoint.new(1, 0),
|
| })
|
| emitter.Lifetime = NumberRange.new(0.3, 0.8)
|
| emitter.Rate = 0
|
| emitter.Speed = NumberRange.new(5, 15)
|
| emitter.SpreadAngle = Vector2.new(180, 180)
|
| emitter.Parent = attachment
|
|
|
| emitter:Emit(12)
|
|
|
| task.delay(1, function()
|
| attachment:Destroy()
|
| end)
|
| end
|
|
|
|
|
| local function dropWoodResource(position, treeType, player)
|
| if math.random() > CraftingConfig.WoodDropChance then return end
|
|
|
| local amount = math.random(CraftingConfig.WoodDropAmount[1], CraftingConfig.WoodDropAmount[2])
|
|
|
| local drop = Instance.new("Part")
|
| drop.Name = "WoodDrop"
|
| drop.Size = Vector3.new(1, 1, 1)
|
| drop.Position = position + Vector3.new(math.random(-3, 3), 2, math.random(-3, 3))
|
| drop.Shape = Enum.PartType.Block
|
| drop.Material = Enum.Material.Wood
|
| drop.Color = CraftingConfig.Resources.Wood.Color
|
| drop.Anchored = false
|
| drop.CanCollide = true
|
| drop.CustomPhysicalProperties = PhysicalProperties.new(0.5, 0.3, 0.5)
|
| drop:SetAttribute("ResourceType", "Wood")
|
| drop:SetAttribute("Amount", amount)
|
| drop:SetAttribute("OwnerId", player.UserId)
|
| CollectionService:AddTag(drop, "ResourceDrop")
|
| drop.Parent = workspace
|
|
|
|
|
| local bb = Instance.new("BillboardGui")
|
| bb.Size = UDim2.new(0, 40, 0, 20)
|
| bb.StudsOffset = Vector3.new(0, 1.5, 0)
|
| bb.AlwaysOnTop = true
|
| bb.Parent = drop
|
|
|
| local label = Instance.new("TextLabel")
|
| label.Size = UDim2.new(1, 0, 1, 0)
|
| label.BackgroundTransparency = 1
|
| label.Text = "+" .. tostring(amount) .. " Wood"
|
| label.TextColor3 = Color3.fromRGB(200, 160, 80)
|
| label.TextScaled = true
|
| label.Font = Enum.Font.GothamBold
|
| label.TextStrokeTransparency = 0.3
|
| label.Parent = bb
|
|
|
|
|
| drop.Touched:Connect(function(hit)
|
| local touchedPlayer = Players:GetPlayerFromCharacter(hit.Parent)
|
| if touchedPlayer and touchedPlayer.UserId == player.UserId then
|
|
|
| local data = _G.GetPlayerData(touchedPlayer)
|
| if data then
|
| if not data.Resources then
|
| data.Resources = {}
|
| end
|
| data.Resources.Wood = (data.Resources.Wood or 0) + amount
|
| NotificationEvent:FireClient(touchedPlayer, "Resource", "+" .. tostring(amount) .. " Wood (Total: " .. tostring(data.Resources.Wood) .. ")")
|
| end
|
| drop:Destroy()
|
| end
|
| end)
|
|
|
|
|
| task.delay(30, function()
|
| if drop.Parent then drop:Destroy() end
|
| end)
|
| end
|
|
|
| local function applyDamage(player, segment, hitPos, damage, weaponType)
|
| if not CollectionService:HasTag(segment, "TreeSegment") then return end
|
|
|
| local treeType = segment:GetAttribute("TreeType")
|
| if not treeType or not ChoppingConfig.TreeTypes[treeType] then return end
|
|
|
| local maxHealth = ChoppingConfig.TreeTypes[treeType].HealthPerSegment
|
| local health = segment:GetAttribute("Health")
|
| if not health then
|
| health = maxHealth
|
| end
|
|
|
| health = health - damage
|
| segment:SetAttribute("Health", health)
|
|
|
|
|
| createChopParticles(hitPos, ChoppingConfig.TreeTypes[treeType].LogColor)
|
|
|
|
|
| if weaponType == "Fist" then
|
| SoundEvent:FireAllClients("AxeSwing", hitPos)
|
| else
|
| SoundEvent:FireAllClients("AxeHitWood", hitPos)
|
| end
|
|
|
|
|
| dropWoodResource(hitPos, treeType, player)
|
|
|
| if health <= 0 then
|
| local segmentSizeY = segment.Size.Y
|
|
|
|
|
| if _G.IncrementStat then
|
| _G.IncrementStat(player, "WoodChopped", 1)
|
| end
|
|
|
|
|
| if _G.ProgressQuest then
|
| _G.ProgressQuest(player, "Chop", 1)
|
| end
|
|
|
|
|
| if _G.CheckAchievements then
|
| task.defer(function() _G.CheckAchievements(player) end)
|
| end
|
|
|
|
|
| segment:SetAttribute("OwnerId", player.UserId)
|
|
|
| if segmentSizeY < ChoppingConfig.MinSegmentSizeY then
|
|
|
| dropWoodResource(segment.Position, treeType, player)
|
| segment:Destroy()
|
| return
|
| end
|
|
|
| local localHitPos = segment.CFrame:ToObjectSpace(CFrame.new(hitPos)).Position
|
| local yPos = localHitPos.Y
|
| local totalHeight = segment.Size.Y
|
| local bottomHeight = (totalHeight / 2) + yPos
|
| local topHeight = totalHeight - bottomHeight
|
|
|
| if bottomHeight < 0.2 or topHeight < 0.2 then
|
| return
|
| end
|
|
|
| local bottomPiece = segment:Clone()
|
| local topPiece = segment:Clone()
|
|
|
| bottomPiece.Parent = segment.Parent
|
| topPiece.Parent = segment.Parent
|
|
|
| bottomPiece.Size = Vector3.new(segment.Size.X, bottomHeight, segment.Size.Z)
|
| topPiece.Size = Vector3.new(segment.Size.X, topHeight, segment.Size.Z)
|
|
|
| local offsetBottom = CFrame.new(0, -topHeight / 2, 0)
|
| local offsetTop = CFrame.new(0, bottomHeight / 2, 0)
|
|
|
| bottomPiece.CFrame = segment.CFrame * offsetBottom
|
| topPiece.CFrame = segment.CFrame * offsetTop
|
|
|
| bottomPiece:SetAttribute("Health", maxHealth)
|
| topPiece:SetAttribute("Health", maxHealth)
|
| bottomPiece:SetAttribute("OwnerId", player.UserId)
|
| topPiece:SetAttribute("OwnerId", player.UserId)
|
|
|
| bottomPiece.Anchored = false
|
| topPiece.Anchored = false
|
|
|
| bottomPiece.CustomPhysicalProperties = PhysicalProperties.new(ChoppingConfig.TreeTypes[treeType].Density, 0.3, 0.5)
|
| topPiece.CustomPhysicalProperties = PhysicalProperties.new(ChoppingConfig.TreeTypes[treeType].Density, 0.3, 0.5)
|
|
|
|
|
| if _G.AddHealthBar then
|
| _G.AddHealthBar(bottomPiece)
|
| _G.AddHealthBar(topPiece)
|
| end
|
|
|
|
|
| SoundEvent:FireAllClients("TreeFall", segment.Position)
|
|
|
| segment:Destroy()
|
| end
|
| end
|
|
|
| local function onChop(player, hitPart, hitPos)
|
|
|
| if _G.IsRateLimited and _G.IsRateLimited(player, "Chop") then return end
|
|
|
| local char = player.Character
|
| if not char or not char:FindFirstChild("Head") then return end
|
|
|
| local axeStats, weaponType = getEquippedAxeOrFist(player)
|
|
|
| local distance = (char.Head.Position - hitPos).Magnitude
|
| if distance > axeStats.Range + 2 then
|
| return
|
| end
|
|
|
| applyDamage(player, hitPart, hitPos, axeStats.Damage, weaponType)
|
| end
|
|
|
| ChopEvent.OnServerEvent:Connect(onChop)
|
|
|