|
|
|
|
|
|
| local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
| local CollectionService = game:GetService("CollectionService")
|
|
|
| local BiomeConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("BiomeConfig"))
|
| local TreeModelConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("TreeModelConfig"))
|
| local ChoppingConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("ChoppingConfig"))
|
|
|
| local treesFolder = Instance.new("Folder")
|
| treesFolder.Name = "Trees"
|
| treesFolder.Parent = workspace
|
|
|
| local treePositions = {}
|
|
|
| local function randomInRange(min, max)
|
| return min + math.random() * (max - min)
|
| end
|
|
|
| local function isPositionValid(pos, minSpacing)
|
| for _, existingPos in ipairs(treePositions) do
|
| local dx = pos.X - existingPos.X
|
| local dz = pos.Z - existingPos.Z
|
| if (dx * dx + dz * dz) < (minSpacing * minSpacing) then
|
| return false
|
| end
|
| end
|
| return true
|
| end
|
|
|
|
|
| local function addHealthBar(segment)
|
| local treeType = segment:GetAttribute("TreeType")
|
| local config = ChoppingConfig.TreeTypes[treeType]
|
| if not config then return end
|
|
|
| local billboard = Instance.new("BillboardGui")
|
| billboard.Name = "HealthBarGui"
|
| billboard.Size = UDim2.new(0, 60, 0, 8)
|
| billboard.StudsOffset = Vector3.new(0, 1, 0)
|
| billboard.AlwaysOnTop = false
|
| billboard.MaxDistance = 30
|
| billboard.Parent = segment
|
|
|
| local bg = Instance.new("Frame")
|
| bg.Name = "Background"
|
| bg.Size = UDim2.new(1, 0, 1, 0)
|
| bg.BackgroundColor3 = Color3.fromRGB(30, 30, 30)
|
| bg.BorderSizePixel = 0
|
| bg.Parent = billboard
|
|
|
| local bgCorner = Instance.new("UICorner")
|
| bgCorner.CornerRadius = UDim.new(0, 3)
|
| bgCorner.Parent = bg
|
|
|
| local fill = Instance.new("Frame")
|
| fill.Name = "Fill"
|
| fill.Size = UDim2.new(1, 0, 1, 0)
|
| fill.BackgroundColor3 = Color3.fromRGB(50, 200, 50)
|
| fill.BorderSizePixel = 0
|
| fill.Parent = bg
|
|
|
| local fillCorner = Instance.new("UICorner")
|
| fillCorner.CornerRadius = UDim.new(0, 3)
|
| fillCorner.Parent = fill
|
|
|
| local label = Instance.new("TextLabel")
|
| label.Name = "HealthText"
|
| label.Size = UDim2.new(1, 0, 1, 0)
|
| label.BackgroundTransparency = 1
|
| label.Text = tostring(config.HealthPerSegment)
|
| label.TextColor3 = Color3.new(1, 1, 1)
|
| label.TextScaled = true
|
| label.Font = Enum.Font.GothamBold
|
| label.TextStrokeTransparency = 0.5
|
| label.Parent = bg
|
|
|
| segment:GetAttributeChangedSignal("Health"):Connect(function()
|
| local maxHP = config.HealthPerSegment
|
| local currentHP = segment:GetAttribute("Health") or maxHP
|
| local ratio = math.clamp(currentHP / maxHP, 0, 1)
|
|
|
| fill.Size = UDim2.new(ratio, 0, 1, 0)
|
| label.Text = tostring(math.ceil(currentHP))
|
|
|
| if ratio > 0.5 then
|
| fill.BackgroundColor3 = Color3.fromRGB(math.floor(255 * (1 - ratio) * 2), 200, 50)
|
| else
|
| fill.BackgroundColor3 = Color3.fromRGB(255, math.floor(200 * ratio * 2), 50)
|
| end
|
| end)
|
| end
|
|
|
|
|
| local function createTreeModel(treeType, position)
|
| local template = TreeModelConfig.Templates[treeType]
|
| if not template then return nil end
|
|
|
| local config = ChoppingConfig.TreeTypes[treeType]
|
| if not config then return nil end
|
|
|
| local model = Instance.new("Model")
|
| model.Name = treeType .. "_Tree"
|
|
|
| local trunkDiameter = randomInRange(template.TrunkDiameter[1], template.TrunkDiameter[2])
|
| local totalHeight = randomInRange(template.TrunkHeight[1], template.TrunkHeight[2])
|
| local segmentCount = math.random(template.SegmentCount[1], template.SegmentCount[2])
|
| local segmentHeight = totalHeight / segmentCount
|
|
|
|
|
| local groundY = 0
|
| local currentY = groundY
|
|
|
| for i = 1, segmentCount do
|
| local segment = Instance.new("Part")
|
| segment.Name = "TrunkSegment_" .. i
|
| segment.Shape = Enum.PartType.Cylinder
|
|
|
| local taperMult = 1 - ((i - 1) / segmentCount) * 0.4
|
| local diameter = trunkDiameter * taperMult
|
|
|
| segment.Size = Vector3.new(segmentHeight, diameter, diameter)
|
| segment.CFrame = CFrame.new(position.X, currentY + segmentHeight / 2, position.Z) * CFrame.Angles(0, 0, math.rad(90))
|
|
|
| segment.Color = template.BarkColor
|
| segment.Material = template.BarkMaterial
|
| segment.Anchored = true
|
| segment.TopSurface = Enum.SurfaceType.Smooth
|
| segment.BottomSurface = Enum.SurfaceType.Smooth
|
|
|
| segment:SetAttribute("TreeType", treeType)
|
| segment:SetAttribute("Health", config.HealthPerSegment)
|
| segment.CustomPhysicalProperties = PhysicalProperties.new(config.Density, 0.3, 0.5)
|
|
|
| CollectionService:AddTag(segment, "TreeSegment")
|
| CollectionService:AddTag(segment, "Draggable")
|
|
|
| if template.GlowTrunk then
|
| local light = Instance.new("PointLight")
|
| light.Color = template.BarkColor
|
| light.Brightness = 0.5
|
| light.Range = 8
|
| light.Parent = segment
|
| end
|
|
|
| addHealthBar(segment)
|
|
|
| segment.Parent = model
|
| currentY = currentY + segmentHeight
|
|
|
| if i > 1 then
|
| local weld = Instance.new("WeldConstraint")
|
| weld.Part0 = model:FindFirstChild("TrunkSegment_" .. (i - 1))
|
| weld.Part1 = segment
|
| weld.Parent = segment
|
| end
|
| end
|
|
|
|
|
| if template.HasLeaves then
|
| local canopyRadius = randomInRange(template.CanopyRadius[1], template.CanopyRadius[2])
|
| local canopyY = currentY
|
|
|
| if template.ConicalCanopy then
|
| for layer = 1, template.CanopySegments do
|
| local layerRadius = canopyRadius * (1 - (layer - 1) / template.CanopySegments)
|
| local canopy = Instance.new("Part")
|
| canopy.Name = "Canopy_" .. layer
|
| canopy.Shape = Enum.PartType.Ball
|
| canopy.Size = Vector3.new(layerRadius * 2, 2, layerRadius * 2)
|
| canopy.Position = Vector3.new(position.X, canopyY + (layer - 1) * 1.5, position.Z)
|
| canopy.Color = template.LeafColor
|
| canopy.Material = template.LeafMaterial
|
| canopy.Anchored = true
|
| canopy.CanCollide = false
|
| canopy.CastShadow = true
|
| canopy.Parent = model
|
| end
|
| else
|
| for seg = 1, template.CanopySegments do
|
| local angle = (seg / template.CanopySegments) * math.pi * 2
|
| local offsetX = math.cos(angle) * canopyRadius * 0.4
|
| local offsetZ = math.sin(angle) * canopyRadius * 0.4
|
| local size = randomInRange(canopyRadius * 0.6, canopyRadius * 1.0)
|
|
|
| local canopy = Instance.new("Part")
|
| canopy.Name = "Canopy_" .. seg
|
| canopy.Shape = Enum.PartType.Ball
|
| canopy.Size = Vector3.new(size * 2, size * 1.2, size * 2)
|
| canopy.Position = Vector3.new(
|
| position.X + offsetX,
|
| canopyY + randomInRange(-1, 2),
|
| position.Z + offsetZ
|
| )
|
| canopy.Color = template.LeafColor
|
| canopy.Material = template.LeafMaterial
|
| canopy.Anchored = true
|
| canopy.CanCollide = false
|
| canopy.CastShadow = true
|
| canopy.Parent = model
|
| end
|
| end
|
| end
|
|
|
| local primaryPart = model:FindFirstChild("TrunkSegment_1")
|
| if primaryPart then
|
| model.PrimaryPart = primaryPart
|
| end
|
|
|
| CollectionService:AddTag(model, "TreeModel")
|
| model.Parent = treesFolder
|
|
|
| return model
|
| end
|
|
|
|
|
| local function spawnTreesInBiome(biomeName, biomeData)
|
| local region = biomeData.Region
|
| local treeTypes = biomeData.TreeTypes
|
| if #treeTypes == 0 then return end
|
|
|
| local regionWidth = region.MaxX - region.MinX
|
| local regionDepth = region.MaxZ - region.MinZ
|
| local area = regionWidth * regionDepth
|
|
|
| local numTrees = math.floor(area * biomeData.TreeDensity)
|
| numTrees = math.min(numTrees, 200)
|
|
|
| local spawned = 0
|
| local attempts = 0
|
| local maxAttempts = numTrees * 5
|
|
|
| while spawned < numTrees and attempts < maxAttempts do
|
| attempts = attempts + 1
|
|
|
| local x = region.MinX + math.random() * regionWidth
|
| local z = region.MinZ + math.random() * regionDepth
|
|
|
| if isPositionValid(Vector3.new(x, 0, z), BiomeConfig.MinTreeSpacing) then
|
| local treeType = treeTypes[math.random(1, #treeTypes)]
|
|
|
| local treePos = Vector3.new(x, 0, z)
|
|
|
| local tree = createTreeModel(treeType, treePos)
|
| if tree then
|
| table.insert(treePositions, treePos)
|
| spawned = spawned + 1
|
| end
|
| end
|
| end
|
|
|
| print("Spawned " .. spawned .. " trees in " .. biomeName)
|
| end
|
|
|
|
|
| task.spawn(function()
|
| while not _G.BiomeGenerationComplete do
|
| task.wait(0.5)
|
| end
|
|
|
| print("=== Tree Spawning Starting ===")
|
|
|
| for biomeName, biomeData in pairs(BiomeConfig.Biomes) do
|
| spawnTreesInBiome(biomeName, biomeData)
|
| task.wait()
|
| end
|
|
|
| print("=== Tree Spawning Complete ===")
|
| _G.TreeSpawningComplete = true
|
| end)
|
|
|
| _G.CreateTree = function(treeType, position)
|
| return createTreeModel(treeType, position)
|
| end
|
|
|
| _G.AddHealthBar = function(segment)
|
| addHealthBar(segment)
|
| end
|
|
|