Загрузка данных



-- CatPlacementServer
-- Помести в ServerScriptService

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local catTools = ServerStorage:WaitForChild("CatTools")

local MAX_CATS = 16
local PICKUP_HOLD_DURATION = 1
local PLACEMENT_DISTANCE = 35

-- Создаем RemoteEvent автоматически
local placeEvent = ReplicatedStorage:FindFirstChild("PlaceCatEvent")

if not placeEvent then
    placeEvent = Instance.new("RemoteEvent")
    placeEvent.Name = "PlaceCatEvent"
    placeEvent.Parent = ReplicatedStorage
end

-- Папка с установленными котами
local placedFolder = workspace:FindFirstChild("PlacedCats")

if not placedFolder then
    placedFolder = Instance.new("Folder")
    placedFolder.Name = "PlacedCats"
    placedFolder.Parent = workspace
end

-- Деньги игрока. Пока без сохранения.
local function setupPlayer(player)
    local leaderstats = player:FindFirstChild("leaderstats")

    if not leaderstats then
        leaderstats = Instance.new("Folder")
        leaderstats.Name = "leaderstats"
        leaderstats.Parent = player
    end

    local coins = leaderstats:FindFirstChild("Coins")

    if not coins then
        coins = Instance.new("NumberValue")
        coins.Name = "Coins"
        coins.Value = 0
        coins.Parent = leaderstats
    end
end

Players.PlayerAdded:Connect(setupPlayer)

for _, player in ipairs(Players:GetPlayers()) do
    setupPlayer(player)
end

local function getOwnedBase(player)
    local baseName = player:GetAttribute("BaseName")

    if not baseName then
        return nil
    end

    local base = workspace:FindFirstChild(baseName)

    if not base or not base:IsA("Model") then
        return nil
    end

    if base:GetAttribute("OwnerUserId") ~= player.UserId then
        return nil
    end

    return base
end

local function countCats(player)
    local count = 0

    for _, cat in ipairs(placedFolder:GetChildren()) do
        if cat:GetAttribute("OwnerUserId") == player.UserId then
            count += 1
        end
    end

    return count
end

local function getEquippedCat(player)
    local character = player.Character

    if not character then
        return nil
    end

    for _, item in ipairs(character:GetChildren()) do
        if item:IsA("Tool")
            and catTools:FindFirstChild(item.Name)
            and item:GetAttribute("Income") ~= nil
            and item:GetAttribute("Rarity") ~= nil then

            return item
        end
    end

    return nil
end

-- Создаем физическую модель установленного кота
local function createPlacedCat(tool, player, floor, clickPosition)
    local catModel = Instance.new("Model")
    catModel.Name = tool.Name

    -- Копируем содержимое Tool, но не его скрипты
    for _, child in ipairs(tool:GetChildren()) do
        if not child:IsA("Script")
            and not child:IsA("LocalScript")
            and not child:IsA("ModuleScript") then

            local clone = child:Clone()
            clone.Parent = catModel
        end
    end

    local handle = catModel:FindFirstChild("Handle", true)

    if not handle or not handle:IsA("BasePart") then
        catModel:Destroy()
        warn("У кота отсутствует Handle:", tool.Name)
        return false
    end

    catModel.PrimaryPart = handle

    -- Закрепляем части, чтобы кот не падал и не разлетался
    for _, descendant in ipairs(catModel:GetDescendants()) do
        if descendant:IsA("BasePart") then
            descendant.Anchored = true
            descendant.CanCollide = false
        end
    end

    -- Позиция на верхней поверхности пола
    local localPosition = floor.CFrame:PointToObjectSpace(clickPosition)

    local surfacePosition = floor.CFrame:PointToWorldSpace(
        Vector3.new(
            localPosition.X,
            floor.Size.Y / 2,
            localPosition.Z
        )
    )

    catModel:PivotTo(CFrame.new(surfacePosition + Vector3.new(0, 3, 0)))

    -- Подгоняем высоту по реальному размеру модели
    local boxCFrame, boxSize = catModel:GetBoundingBox()

    local desiredCenter = surfacePosition
        + Vector3.new(0, boxSize.Y / 2 + 0.05, 0)

    catModel:PivotTo(
        catModel:GetPivot() + (desiredCenter - boxCFrame.Position)
    )

    catModel:SetAttribute("OwnerUserId", player.UserId)
    catModel:SetAttribute("CatToolName", tool.Name)
    catModel:SetAttribute("Income", tool:GetAttribute("Income"))
    catModel:SetAttribute("Rarity", tool:GetAttribute("Rarity"))

    -- Взаимодействие удержанием E
    local prompt = Instance.new("ProximityPrompt")
    prompt.Name = "PickupPrompt"
    prompt.ActionText = "Забрать кота"
    prompt.ObjectText = tool.Name
    prompt.KeyboardKeyCode = Enum.KeyCode.E
    prompt.HoldDuration = PICKUP_HOLD_DURATION
    prompt.MaxActivationDistance = 10
    prompt.RequiresLineOfSight = false
    prompt.Parent = handle

    prompt.Triggered:Connect(function(picker)
        if not catModel.Parent then
            return
        end

        -- Чужих котов забирать нельзя
        if catModel:GetAttribute("OwnerUserId") ~= picker.UserId then
            return
        end

        local backpack = picker:FindFirstChildOfClass("Backpack")
        local template = catTools:FindFirstChild(
            catModel:GetAttribute("CatToolName")
        )

        if not backpack or not template or not template:IsA("Tool") then
            return
        end

        -- Сначала выдаем Tool, потом убираем модель
        local returnedTool = template:Clone()
        returnedTool.Parent = backpack

        catModel:Destroy()
    end)

    catModel.Parent = placedFolder

    return true
end

placeEvent.OnServerEvent:Connect(function(player, target, clickPosition)
    if typeof(target) ~= "Instance"
        or not target:IsA("BasePart")
        or typeof(clickPosition) ~= "Vector3" then
        return
    end

    local base = getOwnedBase(player)

    if not base then
        return
    end

    local floor = base:FindFirstChild("Floor")

    if target ~= floor then
        return
    end

    local character = player.Character
    local root = character and character:FindFirstChild("HumanoidRootPart")

    if not root or (root.Position - clickPosition).Magnitude > PLACEMENT_DISTANCE then
        return
    end

    -- Проверяем, что позиция действительно находится на полу
    local localPosition = floor.CFrame:PointToObjectSpace(clickPosition)

    if math.abs(localPosition.X) > floor.Size.X / 2
        or math.abs(localPosition.Z) > floor.Size.Z / 2
        or math.abs(localPosition.Y - floor.Size.Y / 2) > 5 then
        return
    end

    local tool = getEquippedCat(player)

    if not tool then
        return
    end

    if countCats(player) >= MAX_CATS then
        warn(player.Name .. ": достигнут лимит в 16 котов")
        return
    end

    -- Проверяем, что характеристики заданы корректно
    local income = tool:GetAttribute("Income")
    local rarity = tool:GetAttribute("Rarity")

    if typeof(income) ~= "number"
        or income < 0
        or typeof(rarity) ~= "string" then
        return
    end

    if createPlacedCat(tool, player, floor, clickPosition) then
        tool:Destroy()
    end
end)

-- Начисляем доход раз в секунду
task.spawn(function()
    while true do
        task.wait(1)

        for _, cat in ipairs(placedFolder:GetChildren()) do
            if cat:IsA("Model") then
                local ownerId = cat:GetAttribute("OwnerUserId")
                local income = cat:GetAttribute("Income")
                local owner = ownerId and Players:GetPlayerByUserId(ownerId)

                if owner and typeof(income) == "number" and income > 0 then
                    local leaderstats = owner:FindFirstChild("leaderstats")
                    local coins = leaderstats and leaderstats:FindFirstChild("Coins")

                    if coins then
                        coins.Value += income
                    end
                end
            end
        end
    end
end)

print("CatPlacementServer запущен")