local FieldModule = require(game.ReplicatedStorage.Modules.Field.FieldModule)

local WS = game:GetService("Workspace")
local FolderAtt = WS:FindFirstChild("Attacks")
local DEB = game:GetService("Debris")

local Plant = {}
Plant.__index = Plant

function Plant.new(params: Types.Plant)
	local self = setmetatable({}, Plant)
	self.name = params.name or "Plant"
	self.health = params.health or 100
	self.damage = params.damage or 0
	self.cost = params.cost or 0
	self.rangeOfDamage = params.rangeOfDamage or {x = 1, y = 1}
	self.posOnOnlyOnfield = params.posOnOnlyOnfield or {x = 1, y = 1}
	self.attackDelay = params.attackDelay or 5

	local plantModel = Instance.new("Part")
	plantModel.Name = self.name
	plantModel.Size = Vector3.new(2, 2, 2)
	plantModel.Position = FieldModule.getCellCenter(self.posOnOnlyOnfield.x, self.posOnOnlyOnfield.y)
	plantModel.Anchored = true
	plantModel.Color = Color3.fromRGB(0, 255, 0)

	local plantsFolder = WS:FindFirstChild("Plants")
	if not plantsFolder then
		plantsFolder = Instance.new("Folder")
		plantsFolder.Name = "Plants"
		plantsFolder.Parent = WS
	end

	plantModel.Parent = plantsFolder
	self.model = plantModel
	self:startAttacking()

	return self
end

function Plant:attack()
	local x = self.posOnOnlyOnfield.x
	local y = self.posOnOnlyOnfield.y
	local range = self.rangeOfDamage

	local attackableCells = {}
	for dx = 1, range.x do
		local targetX = x + dx
		local targetY = y
		if FieldModule.isWithinBounds(targetX, targetY) then
			table.insert(attackableCells, {x = targetX, y = targetY})
		end
	end
	for dy = 1, range.y do
		local targetX = x
		local targetY = y + dy
		if FieldModule.isWithinBounds(targetX, targetY) then
			table.insert(attackableCells, {x = targetX, y = targetY})
		end
	end

	for _, cell in ipairs(attackableCells) do
		local entity = FieldModule.getEntityAt(cell.x, cell.y)
		if entity and entity.type == "Zombie" then
			entity:takeDamage(self.damage)
		end

		local cellCenter = FieldModule.getCellCenter(cell.x, cell.y)
		self:createAttackPart(cellCenter)
		print(self.name .. " атакует клетку (" .. cell.x .. ", " .. cell.y .. ")")
	end
end

function Plant:takeDamage(damage: number)
	self.health = self.health - damage
	print(self.name .. " получил " .. damage .. " урона. Осталось здоровья: " .. self.health)
	if self.health <= 0 then
		self:destroy()
	end
end

function Plant:destroy()
	print(self.name .. " уничтожен.")
	if self.model then
		self.model:Destroy()
	end
	FieldModule.freeCell(self.posOnOnlyOnfield.x, self.posOnOnlyOnfield.y)
end

function Plant:createAttackPart(position: Vector3)
	local attackPart = Instance.new("Part")
	attackPart.Size = FieldModule.getCellSize()
	attackPart.Position = position
	attackPart.Anchored = true
	attackPart.CanCollide = false
	attackPart.Color = Color3.fromRGB(255, 0, 0)
	attackPart.Transparency = 0.5
	
	local attacksFolder = FolderAtt
	if not attacksFolder then
		attacksFolder = Instance.new("Folder")
		attacksFolder.Name = "Attacks"
		attacksFolder.Parent = WS
	end

	attackPart.Parent = attacksFolder

	DEB:AddItem(attackPart, 1)
end

function Plant:startAttacking()
	spawn(function()
		while self.health > 0 do
			task.wait(self.attackDelay)
			self:attack()
		end
	end)
end

return Plant

local FieldModule = require(game.ReplicatedStorage.Modules.Field.FieldModule)

local WS = game:GetService("Workspace")
local FolderAtt = WS:FindFirstChild("Attacks")
local DEB = game:GetService("Debris")

local Zombie = {}
Zombie.__index = Zombie

function Zombie.new(params: Types.Zombie)
	local self = setmetatable({}, Zombie)
	self.name = params.name or "Zombie"
	self.health = params.health or 200
	self.speed = params.speed or 1
	self.rangeOfDamage = params.rangeOfDamage or {x = 1, y = 1}
	self.posOnOnlyOnfield = params.posOnOnlyOnfield or {x = 1, y = 1}
	self.attackDelay = params.attackDelay or 3 

	self.type = "Zombie"

	local zombieModel = Instance.new("Part")
	zombieModel.Name = self.name
	zombieModel.Size = Vector3.new(2, 3, 2)
	zombieModel.Position = FieldModule.getCellCenter(self.posOnOnlyOnfield.x, self.posOnOnlyOnfield.y) + Vector3.new(3 * FieldModule.getCellSize().X, 0, 0) 
	zombieModel.Anchored = true
	zombieModel.Color = Color3.fromRGB(128, 128, 128) 

	local zombiesFolder = WS:FindFirstChild("Zombies")
	if not zombiesFolder then
		zombiesFolder = Instance.new("Folder")
		zombiesFolder.Name = "Zombies"
		zombiesFolder.Parent = WS
	end

	zombieModel.Parent = zombiesFolder
	self.model = zombieModel

	FieldModule.occupyCell(self.posOnOnlyOnfield.x, self.posOnOnlyOnfield.y, self)

	self:move()

	return self
end

function Zombie:move()
	spawn(function()
		while self.health > 0 do
			local currentPos = self.model.Position
			local newPos = currentPos - Vector3.new(self.speed * FieldModule.getCellSize().X, 0, 0) 
			self.model.Position = newPos

			local newX = math.floor(newPos.X / FieldModule.getCellSize().X) + 1
			local newY = math.floor(newPos.Z / FieldModule.getCellSize().Z) + 1

			if FieldModule.isWithinBounds(newX, newY) then
				self.posOnOnlyOnfield = {x = newX, y = newY}
			else
				print(self.name .. " вышел за поле! Игрок проиграл.")
				self:destroy()
				break
				
			end

			local entity = FieldModule.getEntityAt(newX, newY)
			if entity and entity.type == "Plant" then
				self:attack(entity)
				break
			end

			task.wait(1 / self.speed)
		end
	end)
end

function Zombie:attack(targetEntity: Types.Plant)
	if targetEntity and targetEntity.takeDamage then
		targetEntity:takeDamage(self.rangeOfDamage.x) 
	end

	self:createAttackPart(FieldModule.getCellCenter(self.posOnOnlyOnfield.x, self.posOnOnlyOnfield.y))
	print(self.name .. " атакует клетку (" .. self.posOnOnlyOnfield.x .. ", " .. self.posOnOnlyOnfield.y .. ")")

	self:startAttacking(targetEntity)
end

function Zombie:takeDamage(damage: number)
	self.health = self.health - damage
	print(self.name .. " получил " .. damage .. " урона. Осталось здоровья: " .. self.health)
	if self.health <= 0 then
		self:destroy()
	end
end

function Zombie:destroy()
	print(self.name .. " уничтожен.")
	if self.model then
		self.model:Destroy()
	end
	FieldModule.freeCell(self.posOnOnlyOnfield.x, self.posOnOnlyOnfield.y)
end

function Zombie:createAttackPart(position: Vector3)
	local attackPart = Instance.new("Part")
	attackPart.Size = FieldModule.getCellSize()
	attackPart.Position = position
	attackPart.Anchored = true
	attackPart.CanCollide = false
	attackPart.Color = Color3.fromRGB(255, 0, 0)
	attackPart.Transparency = 0.5

	local attacksFolder = FolderAtt
	if not attacksFolder then
		attacksFolder = Instance.new("Folder")
		attacksFolder.Name = "Attacks"
		attacksFolder.Parent = WS
	end

	attackPart.Parent = attacksFolder

	DEB:AddItem(attackPart, 1)
end

function Zombie:startAttacking(targetEntity: Types.Plant)
	spawn(function()
		while self.health > 0 and FieldModule.isWithinBounds(self.posOnOnlyOnfield.x, self.posOnOnlyOnfield.y) do
			task.wait(self.attackDelay)
			if targetEntity and targetEntity.health > 0 then
				targetEntity:takeDamage(self.rangeOfDamage.x)
				print(self.name .. " атакует " .. targetEntity.name .. " на клетке (" .. self.posOnOnlyOnfield.x .. ", " .. self.posOnOnlyOnfield.y .. ")")
			else
				break
			end
		end
	end)
end

return Zombie


export type Vector2D = { x: number, y: number }

export type Plant = {
	name: string,
	health: number,
	damage: number,
	cost: number,
	rangeOfDamage: Vector2D,
	posOnOnlyOnfield: Vector2D,
	model: any,
	attackDelay: number,
	attack: () -> (),
	takeDamage: (damage: number) -> (),
	destroy: () -> ()
}

export type Zombie = {
	name: string,
	health: number,
	speed: number,
	rangeOfDamage: Vector2D,
	posOnOnlyOnfield: Vector2D,
	model: any,
	attackDelay: number,
	move: () -> (),
	attack: () -> (),
	takeDamage: (damage: number) -> (),
	destroy: () -> ()
}


local Settings = require(script.Parent.Parent.Utils.Settings)

local WS = game:GetService("Workspace")
local FolderAtt = WS:FindFirstChild("Attacks")
local DEB = game:GetService("Debris")

local FieldModule = {}

local CELL_SIZE = Settings.CELL_SIZE
local FIELD_ROWS = Settings.FIELD_ROWS
local FIELD_COLUMNS = Settings.FIELD_COLUMNS

local fieldState = {}

function FieldModule.newField()
	for row = 1, FIELD_ROWS do
		fieldState[row] = {}
		for col = 1, FIELD_COLUMNS do
			fieldState[row][col] = {
				occupied = false,
				entity = nil
			}
		end
	end
end

function FieldModule.isCellFree(x: number, y: number): boolean
	if fieldState[x] and fieldState[x][y] then
		return not fieldState[x][y].occupied
	end
	return false
end

function FieldModule.isWithinBounds(x: number, y: number): boolean
	return x >= 1 and x <= FIELD_ROWS and y >= 1 and y <= FIELD_COLUMNS
end

function FieldModule.occupyCell(x: number, y: number, entity: any)
	if FieldModule.isWithinBounds(x, y) and FieldModule.isCellFree(x, y) then
		fieldState[x][y].occupied = true
		fieldState[x][y].entity = entity
		return true
	else
		return false
	end
end

function FieldModule.freeCell(x: number, y: number)
	if fieldState[x] and fieldState[x][y] then
		fieldState[x][y].occupied = false
		fieldState[x][y].entity = nil
	end
end

function FieldModule.getCellCenter(x: number, y: number): Vector3
	local posX = (y - 1) * CELL_SIZE.X
	local posZ = (x - 1) * CELL_SIZE.Z
	return Vector3.new(posX, 0, posZ) + Vector3.new(CELL_SIZE.X / 2, 0, CELL_SIZE.Z / 2)
end

function FieldModule.getCellSize()
	return CELL_SIZE
end

function FieldModule.getEntityAt(x: number, y: number)
	if fieldState[x] and fieldState[x][y] then
		return fieldState[x][y].entity
	end
	return nil
end

function FieldModule.visualizeField()
	local fieldFolder = WS:FindFirstChild("Field")
	if not fieldFolder then
		fieldFolder = Instance.new("Folder")
		fieldFolder.Name = "Field"
		fieldFolder.Parent = WS
	end

	for row = 1, FIELD_ROWS do
		for col = 1, FIELD_COLUMNS do
			local part = Instance.new("Part")
			part.Size = CELL_SIZE
			part.Position = FieldModule.getCellCenter(row, col)
			part.Anchored = true
			part.Name = "Cell_" .. row .. "_" .. col
			part.Color = Color3.fromRGB(255, 255, 255)
			part.Transparency = 0.5
			part.Parent = fieldFolder
		end
	end
end

return FieldModule


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local EnemyModule = ReplicatedStorage.Modules.EnemyModule
local FieldModule = require(ReplicatedStorage.Modules.Field.FieldModule)
local Zombie = require(EnemyModule.Zombie)
local Plant = require(EnemyModule.Plant)

local PlacePlantEvent = ReplicatedStorage:WaitForChild("RemoteEvents"):WaitForChild("PlacePlantEvent")

local WS = game:GetService("Workspace")
local FolderAtt = WS:FindFirstChild("Attacks")
local DEB = game:GetService("Debris")

FieldModule.newField()
FieldModule.visualizeField()

local attacksFolder = FolderAtt
if not attacksFolder then
	attacksFolder = Instance.new("Folder")
	attacksFolder.Name = "Attacks"
	attacksFolder.Parent = WS
end

local plants = {}
local zombies = {}

local playerResources = {}

Players.PlayerAdded:Connect(function(player)
	playerResources[player.UserId] = {
		sunPoints = 10000
	}
end)

local function placePlant(player, x, y, plantParams)
	local resources = playerResources[player.UserId]
	if not resources then
		print("Игрок не найден.")
		return
	end

	if resources.sunPoints >= plantParams.cost then
		if FieldModule.occupyCell(x, y, plantParams) then
			local plant = Plant.new(plantParams)
			table.insert(plants, plant)
			resources.sunPoints = resources.sunPoints - plantParams.cost
			print(plant.name .. " размещен на клетке (" .. x .. ", " .. y .. "). Осталось солнечных очков: " .. resources.sunPoints)
		else
			print("Клетка (" .. x .. ", " .. y .. ") уже занята.")
		end
	else
		print("Недостаточно солнечных очков для размещения " .. plantParams.name)
	end
end

PlacePlantEvent.OnServerEvent:Connect(function(player, x, y, plantParams)
	placePlant(player, x, y, plantParams)
end)

local function attackPlants()
	while true do
		task.wait(1)
		for _, plant in ipairs(plants) do
			if plant.health > 0 then
				plant:attack()
			end
		end
	end
end

spawn(attackPlants)

local function spawnZombie()
	local zombieParams = {
		name = "Regular Zombie",
		health = 200,
		speed = 1,
		rangeOfDamage = {x = 10, y = 0},
		posOnOnlyOnfield = {x = 3, y = 9},
		attackDelay = 3
	}

	if FieldModule.isCellFree(zombieParams.posOnOnlyOnfield.x, zombieParams.posOnOnlyOnfield.y) then
		local zombie = Zombie.new(zombieParams)
		table.insert(zombies, zombie)
		print(zombie.name .. " спавнится на клетке (" .. zombie.posOnOnlyOnfield.x .. ", " .. zombie.posOnOnlyOnfield.y .. ")")
	else
		print("Клетка (" .. zombieParams.posOnOnlyOnfield.x .. ", " .. zombieParams.posOnOnlyOnfield.y .. ") занята. Зомби не может спавниться.")
	end
end

spawn(function()
	while true do
		task.wait(3)
		spawnZombie()
	end
end)

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local FieldModule = require(ReplicatedStorage.Modules.Field.FieldModule)

local DebugModule = {}

function DebugModule.checkCell(x: number, y: number)
	if FieldModule.isWithinBounds(x, y) then
		if FieldModule.isCellFree(x, y) then
			print("Клетка (" .. x .. ", " .. y .. ") свободна.")
		else
			local entity = FieldModule.getEntityAt(x, y)
			print("Клетка (" .. x .. ", " .. y .. ") занята " .. (entity and entity.name or "нет информации") .. ".")
		end
	else
		print("Клетка (" .. x .. ", " .. y .. ") находится за пределами поля.")
	end
end

return DebugModule


local Settings = {
	
	CELL_SIZE = Vector3.new(3, 0.5, 3),
	FIELD_ROWS = 5,
	FIELD_COLUMNS = 9,
	
}

return Settings


-- StarterPlayer.StarterPlayerScripts.LocalDebugScript.lua

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

-- Убедитесь, что используете правильный путь к FieldModule
local FieldModule = require(ReplicatedStorage.Modules.Field.FieldModule) 
local PlacePlantEvent = ReplicatedStorage:WaitForChild("RemoteEvents"):WaitForChild("PlacePlantEvent")

local player = Players.LocalPlayer
local mouse = player:GetMouse()

local playerGui = player:WaitForChild("PlayerGui")

-- Проверка и создание ScreenGui
local screenGui = playerGui:FindFirstChild("ScreenGui")
if not screenGui then
	screenGui = Instance.new("ScreenGui")
	screenGui.Name = "ScreenGui"
	screenGui.Parent = playerGui
end

-- Проверка и создание TextLabel
local statusLabel = screenGui:FindFirstChild("TextLabel")
if not statusLabel then
	statusLabel = Instance.new("TextLabel")
	statusLabel.Name = "TextLabel"
	statusLabel.Size = UDim2.new(0, 200, 0, 50)
	statusLabel.Position = UDim2.new(0.5, -100, 0, 10)
	statusLabel.BackgroundTransparency = 0.5
	statusLabel.BackgroundColor3 = Color3.new(0, 0, 0)
	statusLabel.TextColor3 = Color3.new(1, 1, 1)
	statusLabel.TextScaled = true
	statusLabel.Visible = false
	statusLabel.Parent = screenGui
end

print("LocalDebugScript загружен:", 234234234)

-- Функция для обновления статуса клетки
local function updateCellStatus(target)
	if target and target.Name:match("^Cell_(%d+)_(%d+)$") then
		local xStr, yStr = target.Name:match("Cell_(%d+)_(%d+)$")

		local x = tonumber(xStr)
		local y = tonumber(yStr)
		print("Координаты клетки:", x, y)
		if x and y then
			print("Проверка возможности размещения растения...")
			local isFree = FieldModule.isCellFree(x, y)
			if isFree then
				statusLabel.Text = "Можно поставить растение"
				statusLabel.BackgroundColor3 = Color3.fromRGB(0, 255, 0) -- Зеленый
			else
				statusLabel.Text = "Нельзя поставить растение"
				statusLabel.BackgroundColor3 = Color3.fromRGB(255, 0, 0) -- Красный
			end
			statusLabel.Visible = true

			print("Визуальное выделение клетки")
			target.Color = isFree and Color3.fromRGB(0, 255, 0) or Color3.fromRGB(255, 0, 0)
		end
	else
		statusLabel.Visible = false
		-- Сброс цвета клетки при уходе курсора
		if mouse.Target and mouse.Target.Name:match("^Cell_(%d+)_(%d+)$") then
			local xStr, yStr = mouse.Target.Name:match("Cell_(%d+)_(%d+)$")
			local x = tonumber(xStr)
			local y = tonumber(yStr)
			if x and y then
				-- Восстановить исходный цвет клетки (например, белый)
				mouse.Target.Color = Color3.fromRGB(255, 255, 255)
			end
		end
	end
end

-- Обработчик события движения мыши
mouse.Move:Connect(function()
	updateCellStatus(mouse.Target)
	print("Обработчик движения мыши вызван")
end)

-- Обработчик события клика мыши для попытки размещения растения
mouse.Button1Down:Connect(function()
	local target = mouse.Target
	if target and target.Name:match("^Cell_(%d+)_(%d+)$") then
		local xStr, yStr = target.Name:match("Cell_(%d+)_(%d+)$")
		local x = tonumber(xStr)
		local y = tonumber(yStr)
		if x and y then
			-- Отправляем запрос на размещение растения на сервер
			local plantParams = {
				name = "Sunflower", -- Пример: тип растения. Можно расширить для выбора.
				health = 100,
				damage = 20, -- Урон растения
				cost = 50,
				rangeOfDamage = {x = 1, y = 1}, -- Диапазон атаки
				posOnOnlyOnfield = {x = x, y = y},
				attackDelay = 5 -- Задержка атаки растения
			}
			PlacePlantEvent:FireServer(x, y, plantParams)
			print("Запрос на размещение растения отправлен:", x, y)
		end
	end
end)


клетки всеглда считаются зангятыми даже когда я ничего ен ставлю исправь и зобми при спавне сразу имирают сдлеай чтобы клетки изначально были пусты и только игрок мог савить растения а зомби также спавнились и шли