<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
<Item class="Workspace" referent="RBX1"><Properties><string name="Name">Workspace</string></Properties></Item>
<Item class="Lighting" referent="RBX2"><Properties><string name="Name">Lighting</string></Properties></Item>
<Item class="ReplicatedStorage" referent="RBX3"><Properties><string name="Name">ReplicatedStorage</string></Properties></Item>
<Item class="ServerScriptService" referent="RBX7"><Properties><string name="Name">ServerScriptService</string></Properties><Item class="Script" referent="RBX4"><Properties><string name="Name">OldHouse</string><bool name="Disabled">false</bool><token name="RunContext">0</token><ProtectedString name="Source"><![CDATA[--[[
	СТАРЫЙ ДОМ: ПОДВАЛ + 2 ЭТАЖА + ЧЕРДАК (Script → ServerScriptService)
	Lighting.Technology поставь на Future (Explorer → Lighting → Technology).
]]
local Lighting = game:GetService("Lighting")
local TweenService = game:GetService("TweenService")

---------------------------------------------------------------- НАСТРОЙКИ (крути тут)
local LAMP_BRIGHTNESS = 3
local LAMP_RANGE      = 26
local FIRE_BRIGHTNESS = 2.5
local AMBIENT  = Color3.fromRGB(40, 38, 36)
local OUTDOOR  = Color3.fromRGB(80, 85, 100)
local EXPOSURE = 0.4
local FOG      = 0.25
local FLICKER  = true
local SOUND_AMBIENT = "" -- "rbxassetid://..." гул/ветер
local SOUND_DOOR    = "" -- скрип двери

local BY, L1, L2, L3 = -9.2, 1.4, 12, 22.6 -- полы: подвал, 1 этаж, 2 этаж, чердак
local H, T = 10, 0.6                        -- высота стен, толщина перекрытия
local KW, R = 5, 8                          -- стенки чердака, высота крыши

local C = {
	wall = Color3.fromRGB(78, 70, 62), floor = Color3.fromRGB(60, 44, 32),
	wood = Color3.fromRGB(72, 52, 36), darkWood = Color3.fromRGB(42, 30, 22),
	roof = Color3.fromRGB(38, 36, 38), stone = Color3.fromRGB(72, 70, 68),
	fabric = Color3.fromRGB(70, 32, 30), ground = Color3.fromRGB(38, 40, 30),
	metal = Color3.fromRGB(60, 58, 55),
}
local LAMP_COLOR = Color3.fromRGB(255, 205, 150)
local BULB_ON  = Color3.fromRGB(255, 220, 170)
local BULB_OFF = Color3.fromRGB(60, 50, 40)
local M = Enum.Material
local rng = Random.new(1337)

local old = workspace:FindFirstChild("OldHouse")
if old then old:Destroy() end
local house = Instance.new("Model")
house.Name = "OldHouse"
house.Parent = workspace

local function P(size, cf, color, mat, props)
	local p = Instance.new(props and props.Class or "Part")
	if props and props.Shape then p.Shape = props.Shape end
	p.Anchored = true
	p.Size = size
	p.CFrame = cf
	p.Color = color or C.wood
	p.Material = mat or M.Wood
	p.TopSurface = Enum.SurfaceType.Smooth
	p.BottomSurface = Enum.SurfaceType.Smooth
	if props then
		for k, v in pairs(props) do
			if k ~= "Class" and k ~= "Shape" then p[k] = v end
		end
	end
	if not p.Parent then p.Parent = house end
	return p
end
-- коробка по двум углам
local function B(x0, y0, z0, x1, y1, z1, color, mat, props)
	if x1 - x0 < 0.05 or y1 - y0 < 0.05 or z1 - z0 < 0.05 then return end
	return P(Vector3.new(x1 - x0, y1 - y0, z1 - z0), CFrame.new((x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2), color, mat, props)
end
local CYL, BALL = Enum.PartType.Cylinder, Enum.PartType.Ball
local function rad(d) return math.rad(d) end

---------------------------------------------------------------- ЗЕМЛЯ С ЯМОЙ ПОД ПОДВАЛ
local bp = workspace:FindFirstChild("Baseplate")
if bp then bp:Destroy() end
for _, r in ipairs({{-300, 300, -300, -15.5}, {-300, 300, 15.5, 300}, {-300, -20.5, -15.5, 15.5}, {20.5, 300, -15.5, 15.5}}) do
	B(r[1], -20, r[3], r[2], 0, r[4], C.ground, M.Ground, {Name = "Ground"})
end
B(-20.5, -20, -15.5, 20.5, BY, 15.5, C.stone, M.Slate, {Name = "BasementFloor"})

---------------------------------------------------------------- ПЕРЕКРЫТИЯ С ПРОЁМАМИ ПОД ЛЕСТНИЦЫ
local function slab(y, hx0, hx1, hz0, hz1)
	local y0 = y - T
	B(-20.5, y0, -15.5, 20.5, y, hz0, C.floor, M.WoodPlanks, {Name = "Floor"})
	B(-20.5, y0, hz1, 20.5, y, 15.5, C.floor, M.WoodPlanks, {Name = "Floor"})
	B(-20.5, y0, hz0, hx0, y, hz1, C.floor, M.WoodPlanks, {Name = "Floor"})
	B(hx1, y0, hz0, 20.5, y, hz1, C.floor, M.WoodPlanks, {Name = "Floor"})
end
slab(L1, -19.5, -15.5, -10.8, 4)   -- над лестницей из подвала
slab(L2, 15.5, 19.5, -4.5, 9.8)    -- над лестницей на 2 этаж
slab(L3, -19.5, -15.5, -10.8, 4)   -- над лестницей на чердак

-- перила вокруг проёма: sides = список {x0,z0,x1,z1}
local function rail(y, sides)
	for _, s in ipairs(sides) do
		local x0, z0, x1, z1 = s[1], s[2], s[3], s[4]
		local len = math.max(math.abs(x1 - x0), math.abs(z1 - z0))
		local along = Vector3.new(x1 - x0, 0, z1 - z0).Unit
		for i = 0, math.floor(len / 1.6) do
			local p = Vector3.new(x0, y, z0) + along * math.min(i * 1.6, len)
			P(Vector3.new(0.25, 3, 0.25), CFrame.new(p + Vector3.new(0, 1.5, 0)), C.darkWood)
		end
		local mid = Vector3.new((x0 + x1) / 2, y + 3.1, (z0 + z1) / 2)
		P(Vector3.new(0.35, 0.25, len + 0.3), CFrame.lookAt(mid, mid + along), C.wood, M.Wood, {Name = "Rail"})
	end
end
rail(L1, {{-15.4, -10.8, -15.4, 4.1}, {-19.5, 4.1, -15.4, 4.1}})
rail(L2, {{15.4, -4.6, 15.4, 9.8}, {15.4, -4.6, 19.5, -4.6}})
rail(L3, {{-15.4, -10.8, -15.4, 4.1}, {-19.5, 4.1, -15.4, 4.1}})

---------------------------------------------------------------- ЛЕСТНИЦЫ (16 ступеней)
local STEPS, RISE, TREAD = 16, (L2 - L1) / 16, 1.3
local function stairs(x0, x1, zStart, dir, yBase, mat)
	for i = 1, STEPS do
		local za = zStart + dir * (i - 1) * TREAD
		local zb = za + dir * TREAD
		local top = yBase + i * RISE
		B(x0, top - math.min(i * RISE, 1.2), math.min(za, zb), x1, top, math.max(za, zb), mat == M.Concrete and C.stone or C.wood, mat, {Name = "Step"})
		B(x0 + 0.05, top - 0.05, math.min(za, zb) - 0.05, x1 - 0.05, top + 0.05, math.max(za, zb) + 0.05, C.darkWood, M.WoodPlanks)
	end
	-- косоур (боковая балка) снизу
	local len = STEPS * TREAD
	local a = math.atan(RISE / TREAD)
	local cx = x1 - 0.2
	local mid = Vector3.new(cx, yBase + STEPS * RISE / 2 - 0.6, zStart + dir * len / 2)
	P(Vector3.new(0.4, 1.2, math.sqrt(len ^ 2 + (STEPS * RISE) ^ 2)), CFrame.new(mid) * CFrame.Angles(-dir * a, 0, 0), C.darkWood)
end
stairs(-19.5, -15.5, 10, -1, BY, M.Concrete) -- подвал → 1 этаж
stairs(15.5, 19.5, -11, 1, L1, M.Wood)       -- 1 → 2
stairs(-19.5, -15.5, 10, -1, L2, M.Wood)     -- 2 → чердак

---------------------------------------------------------------- СТЕНЫ
local function wall(cf, length, holes, h, col, mat)
	h = h or H
	table.sort(holes, function(a, b) return a.x < b.x end)
	local function seg(x0, x1, y0, y1)
		if x1 - x0 < 0.05 or y1 - y0 < 0.05 then return end
		P(Vector3.new(x1 - x0, y1 - y0, 1), cf * CFrame.new((x0 + x1) / 2, (y0 + y1) / 2, 0), col or C.wall, mat or M.WoodPlanks, {Name = "Wall"})
	end
	local cursor = -length / 2
	for _, o in ipairs(holes) do
		local a, b = o.x - o.w / 2, o.x + o.w / 2
		seg(cursor, a, 0, h)
		seg(a, b, 0, o.y0)
		seg(a, b, o.y1, h)
		cursor = b
	end
	seg(cursor, length / 2, 0, h)
end
local function win(x) return {x = x, w = 4, y0 = 3.5, y1 = 7.5} end
local function doorHole(x, w) return {x = x, w = w, y0 = 0, y1 = 8} end
local ROT = CFrame.Angles(0, rad(90), 0)

local function windowDeco(cf, x, outSign, boarded)
	local y0, y1, w = 3.5, 7.5, 4
	local cy, hgt = (y0 + y1) / 2, y1 - y0
	P(Vector3.new(w + 0.6, 0.25, 1.6), cf * CFrame.new(x, y0 - 0.12, 0), C.darkWood)
	P(Vector3.new(0.2, hgt, 0.4), cf * CFrame.new(x, cy, 0), C.darkWood)
	P(Vector3.new(w, 0.2, 0.4), cf * CFrame.new(x, cy, 0), C.darkWood)
	P(Vector3.new(w, hgt, 0.1), cf * CFrame.new(x, cy, 0), Color3.fromRGB(55, 65, 60), M.Glass, {Transparency = 0.55, Name = "Glass"})
	if boarded then
		for i, ang in ipairs({18, -14, 9}) do
			P(Vector3.new(w + 1.2, 0.55, 0.15), cf * CFrame.new(x, y0 + hgt * i / 4, 0.6 * outSign) * CFrame.Angles(0, 0, rad(ang)), C.wood, M.WoodPlanks, {Name = "Board"})
		end
	end
end
-- одна функция на этаж: стены + окна; wins = {front={...}, back={...}, left={...}, right={...}}
local function storey(y, wins, frontDoor)
	local sides = {
		{CFrame.new(0, y, -15), 41, wins.front, -1}, {CFrame.new(0, y, 15), 41, wins.back, 1},
		{CFrame.new(-20, y, 0) * ROT, 30, wins.left, -1}, {CFrame.new(20, y, 0) * ROT, 30, wins.right, 1},
	}
	for i, s in ipairs(sides) do
		local holes = {}
		for _, x in ipairs(s[3]) do table.insert(holes, win(x)) end
		if i == 1 and frontDoor then table.insert(holes, doorHole(0, 4.5)) end
		wall(s[1], s[2], holes)
		for _, x in ipairs(s[3]) do windowDeco(s[1], x, s[4], rng:NextNumber() < 0.5) end
	end
end
-- подвал: камень
for _, cf in ipairs({CFrame.new(0, BY, -15), CFrame.new(0, BY, 15)}) do wall(cf, 41, {}, H - T, C.stone, M.Cobblestone) end
for _, cf in ipairs({CFrame.new(-20, BY, 0) * ROT, CFrame.new(20, BY, 0) * ROT}) do wall(cf, 30, {}, H - T, C.stone, M.Cobblestone) end
storey(L1, {front = {-12, 12}, back = {-12, 12}, left = {-6}, right = {}}, true)
storey(L2, {front = {-12, 4, 12}, back = {-8, 8}, left = {}, right = {-8, 6}})
-- перегородки
wall(CFrame.new(0, L1, 3), 31, {doorHole(-8, 4), doorHole(7, 5)})
wall(CFrame.new(-3, L2, -4.5) * ROT, 20, {doorHole(0, 4)})
-- подпорки в подвале
for _, x in ipairs({-7, 7}) do
	for _, z in ipairs({-5, 5}) do B(x - 0.6, BY, z - 0.6, x + 0.6, L1 - T, z + 0.6, C.darkWood, M.Wood, {Name = "Post"}) end
end

---------------------------------------------------------------- ЧЕРДАК И КРЫША
local TOP = L3 + KW
wall(CFrame.new(0, L3, -15), 41, {}, KW)
wall(CFrame.new(0, L3, 15), 41, {}, KW)
for _, x in ipairs({-20, 20}) do
	B(x - 0.5, L3, -15.5, x + 0.5, TOP, 15.5, C.wall, M.WoodPlanks, {Name = "Wall"})
	P(Vector3.new(1, R, 15), CFrame.new(x, TOP + R / 2, -7.5), C.wall, M.WoodPlanks, {Class = "WedgePart", Name = "Gable"})
	P(Vector3.new(1, R, 15), CFrame.new(x, TOP + R / 2, 7.5) * CFrame.Angles(0, math.pi, 0), C.wall, M.WoodPlanks, {Class = "WedgePart", Name = "Gable"})
end
local ra = math.atan(R / 15)
local RL = math.sqrt(15 ^ 2 + R ^ 2) + 1.5
local ridge = Vector3.new(0, TOP + R, 0)
for _, s in ipairs({-1, 1}) do
	local rot = CFrame.Angles(s * ra, 0, 0)
	local down = Vector3.new(0, -math.sin(ra), s * math.cos(ra))
	P(Vector3.new(44, 0.8, RL), CFrame.new(ridge + down * (RL / 2) + rot.UpVector * 0.4) * rot, C.roof, M.Slate, {Name = "Roof"})
end
P(Vector3.new(44, 0.6, 1.2), CFrame.new(0, TOP + R + 0.5, 0), C.roof, M.Slate, {Name = "RoofRidge"})
for x = -16, 16, 8 do P(Vector3.new(0.6, 0.8, 30), CFrame.new(x, TOP + 0.4, 0), C.darkWood) end -- балки

-- крыльцо
B(-4, 0, -17.5, 4, 0.7, -15.5, C.stone, M.Concrete)
B(-4, 0, -16.5, 4, L1, -15.5, C.stone, M.Concrete)

---------------------------------------------------------------- ДВЕРИ
local function door(hingeCF, w, h, openAngle)
	local d = P(Vector3.new(w, h, 0.3), hingeCF * CFrame.new(w / 2, h / 2, 0), C.darkWood, M.Wood, {Name = "Door"})
	local knob = P(Vector3.new(0.3, 0.3, 0.7), d.CFrame * CFrame.new(w / 2 - 0.45, -0.4, 0), Color3.fromRGB(110, 90, 50), M.Metal, {Name = "Knob", CanCollide = false, Massless = true})
	local weld = Instance.new("WeldConstraint")
	weld.Part0, weld.Part1, weld.Parent = d, knob, d
	knob.Anchored = false
	local mod = Instance.new("PathfindingModifier") -- монстры проходят сквозь двери
	mod.PassThrough = true
	mod.Parent = d
	local closedCF = d.CFrame
	local openCF = hingeCF * CFrame.Angles(0, rad(openAngle), 0) * CFrame.new(w / 2, h / 2, 0)
	local sound
	if SOUND_DOOR ~= "" then
		sound = Instance.new("Sound")
		sound.SoundId, sound.Volume, sound.RollOffMaxDistance, sound.Parent = SOUND_DOOR, 0.8, 50, d
	end
	local prompt = Instance.new("ProximityPrompt")
	prompt.ActionText, prompt.ObjectText = "Открыть", "Дверь"
	prompt.MaxActivationDistance, prompt.RequiresLineOfSight = 8, false
	prompt.Parent = d
	local isOpen = false
	prompt.Triggered:Connect(function()
		isOpen = not isOpen
		prompt.ActionText = isOpen and "Закрыть" or "Открыть"
		TweenService:Create(d, TweenInfo.new(1.4, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), {CFrame = isOpen and openCF or closedCF}):Play()
		if sound then sound:Play() end
	end)
end
door(CFrame.new(-2.25, L1, -15), 4.5, 8, -100)
door(CFrame.new(-10, L1, 3), 4, 8, -100)
door(CFrame.new(-3, L2, -2.5) * ROT, 4, 8, 100)

---------------------------------------------------------------- МЕБЕЛЬ
local function tableAt(x, y, z, w, d, hgt)
	B(x - w / 2, y + hgt - 0.3, z - d / 2, x + w / 2, y + hgt, z + d / 2, C.wood, M.WoodPlanks, {Name = "Table"})
	for _, sx in ipairs({-1, 1}) do for _, sz in ipairs({-1, 1}) do
		local lx, lz = x + sx * (w / 2 - 0.3), z + sz * (d / 2 - 0.3)
		B(lx - 0.15, y, lz - 0.15, lx + 0.15, y + hgt - 0.3, lz + 0.15, C.darkWood)
	end end
end
local function chair(cf)
	P(Vector3.new(1.8, 0.25, 1.8), cf * CFrame.new(0, 1.9, 0), C.wood)
	P(Vector3.new(1.8, 2.2, 0.2), cf * CFrame.new(0, 3.1, 0.8), C.wood)
	for _, sx in ipairs({-0.75, 0.75}) do for _, sz in ipairs({-0.75, 0.75}) do
		P(Vector3.new(0.2, 1.8, 0.2), cf * CFrame.new(sx, 0.9, sz), C.darkWood)
	end end
end
local function bed(x, y, z)
	B(x - 3, y, z - 5, x + 3, y + 1.6, z + 5, C.darkWood, M.Wood, {Name = "Bed"})
	B(x - 2.9, y + 1.6, z - 4.9, x + 2.9, y + 2.4, z + 4.9, Color3.fromRGB(150, 140, 125), M.Fabric)
	B(x - 2.3, y + 2.4, z + 3, x + 2.3, y + 3, z + 4.6, Color3.fromRGB(170, 165, 150), M.Fabric)
	B(x - 3, y, z + 4.8, x + 3, y + 4.5, z + 5.2, C.darkWood)
	B(x - 2.95, y + 2.4, z - 4.6, x + 2.95, y + 2.6, z + 1.5, C.fabric, M.Fabric) -- одеяло
end
local function shelf(x, y, z, alongX)
	local w, d = alongX and 6 or 1.6, alongX and 1.6 or 6
	for k = 0, 3 do B(x - w / 2, y + k * 2, z - d / 2, x + w / 2, y + k * 2 + 0.25, z + d / 2, C.wood) end
	for _, s in ipairs({-1, 1}) do
		local ox, oz = alongX and s * w / 2 or 0, alongX and 0 or s * d / 2
		P(Vector3.new(alongX and 0.25 or d, 6.5, alongX and d or 0.25), CFrame.new(x + ox, y + 3.25, z + oz), C.darkWood)
	end
	for k = 1, 3 do
		for j = 1, 3 do
			local off = (j - 2) * 1.6
			P(Vector3.new(0.9, 0.6, 0.6), CFrame.new(x + (alongX and off or 0), y + k * 2 + 0.7, z + (alongX and 0 or off)) * CFrame.Angles(0, 0, rad(90)), Color3.fromHSV(rng:NextNumber(), 0.3, 0.35), M.Glass, {Shape = CYL, Transparency = 0.3, CanCollide = false})
		end
	end
end
local function crate(x, y, z, s)
	P(Vector3.new(s, s, s), CFrame.new(x, y + s / 2, z) * CFrame.Angles(0, rad(rng:NextInteger(0, 40)), 0), C.wood, M.WoodPlanks, {Name = "Crate"})
end
local function barrel(x, y, z)
	P(Vector3.new(3, 2.4, 2.4), CFrame.new(x, y + 1.5, z) * CFrame.Angles(0, 0, rad(90)), C.wood, M.Wood, {Shape = CYL, Name = "Barrel"})
	for _, dy in ipairs({0.4, 2.6}) do P(Vector3.new(0.2, 2.5, 2.5), CFrame.new(x, y + dy, z) * CFrame.Angles(0, 0, rad(90)), C.metal, M.Metal, {Shape = CYL}) end
end

-- ПОДВАЛ: котёл, бочки, стеллажи, трубы, цепи
B(4, BY, 11, 10, BY + 6, 14.5, C.metal, M.CorrodedMetal, {Name = "Boiler"})
P(Vector3.new(2, 1, 0.1), CFrame.new(7, BY + 3, 10.9), Color3.fromRGB(255, 90, 30), M.Neon, {Name = "BoilerGlow"})
P(Vector3.new(36, 0.8, 0.8), CFrame.new(0, L1 - T - 0.8, 13.5), C.metal, M.CorrodedMetal, {Shape = CYL, Name = "Pipe"})
barrel(-12, BY, 12); barrel(-9, BY, 12.5); barrel(14, BY, -12)
shelf(17.5, BY, 0, false); shelf(0, BY, -14, true)
crate(-10, BY, -11, 3); crate(-8, BY, -12, 2); crate(12, BY, 8, 3)
for _, x in ipairs({-3, 3}) do
	for i = 0, 6 do P(Vector3.new(0.25, 0.6, 0.15), CFrame.new(x, L1 - T - 0.4 - i * 0.55, 0) * CFrame.Angles(0, (i % 2) * rad(90), 0), C.metal, M.Metal) end
end
tableAt(10, BY, -6, 6, 3, 3.2)

-- 1 ЭТАЖ: гостиная спереди, кухня и камин сзади
B(-11, L1, -14.4, -3, L1 + 2, -11.5, C.fabric, M.Fabric, {Name = "Sofa"})
B(-11, L1, -14.4, -3, L1 + 4.2, -13.6, C.fabric, M.Fabric)
tableAt(-7, L1, -8, 4, 2.5, 1.6)
tableAt(4, L1, -6, 6, 4, 3.2)
chair(CFrame.new(4, L1, -9.2)); chair(CFrame.new(4, L1, -2.8) * CFrame.Angles(0, math.pi, 0))
P(Vector3.new(1.8, 0.25, 1.8), CFrame.new(8, L1 + 0.2, -6) * CFrame.Angles(rad(80), 0, rad(30)), C.wood) -- упавший стул
B(-10, L1, 13, -2, L1 + 7, 14.5, C.stone, M.Brick, {Name = "Fireplace"})
B(-8, L1, 12.4, -4, L1 + 3.5, 14.4, Color3.fromRGB(20, 18, 16), M.Slate, {Name = "FirePit"})
B(-10.5, L1 + 6.5, 12.2, -1.5, L1 + 7, 14.5, C.darkWood)
B(2, L1, 12.5, 14, L1 + 3.4, 14.5, C.wood, M.WoodPlanks, {Name = "Counter"})
B(2, L1 + 3.4, 12.3, 14, L1 + 3.7, 14.5, C.stone, M.Marble)
tableAt(7, L1, 7.5, 5, 3, 3.2)
chair(CFrame.new(5, L1, 5) ); chair(CFrame.new(9.5, L1, 10) * CFrame.Angles(0, math.pi, 0))
shelf(-14, L1, 14, true)

-- 2 ЭТАЖ: спальня, детская, кабинет
bed(-10, L2, -9)
B(-15, L2, 8, -9, L2 + 8, 10, C.darkWood, M.Wood, {Name = "Wardrobe"})
B(6, L2, -14.4, 12, L2 + 3.2, -12, C.wood, M.WoodPlanks, {Name = "Desk"})
chair(CFrame.new(9, L2, -10.5) * CFrame.Angles(0, math.pi, 0))
B(2, L2, 12, 8, L2 + 2.5, 14.4, Color3.fromRGB(120, 110, 95), M.Fabric, {Name = "Crib"})
P(Vector3.new(1.4, 1.4, 1.4), CFrame.new(5, L2 + 3.2, 13), Color3.fromRGB(150, 120, 90), M.Fabric, {Shape = BALL, Name = "Teddy"})
shelf(12, L2, 14, true)
B(-1, L2, 2, 1, L2 + 0.1, 6, C.fabric, M.Fabric) -- ковёр

-- ЧЕРДАК: коробки, манекен, кресло-качалка
for i = 1, 8 do crate(rng:NextNumber(-12, 14), L3, rng:NextNumber(-10, 10), rng:NextNumber(1.5, 3)) end
P(Vector3.new(1.4, 3, 1), CFrame.new(10, L3 + 4.5, -6), Color3.fromRGB(200, 190, 170), M.SmoothPlastic, {Name = "Mannequin"})
P(Vector3.new(1.1, 1.1, 1.1), CFrame.new(10, L3 + 6.6, -6), Color3.fromRGB(200, 190, 170), M.SmoothPlastic, {Shape = BALL})
P(Vector3.new(0.3, 3, 0.3), CFrame.new(10, L3 + 1.5, -6), C.darkWood)
chair(CFrame.new(-6, L3, 6) * CFrame.Angles(rad(-6), rad(200), 0))

---------------------------------------------------------------- СВЕТ
local lamps, flames = {}, {}
local function lamp(x, ceilY, z, cord, bright)
	cord = cord or 1.5
	P(Vector3.new(0.08, cord, 0.08), CFrame.new(x, ceilY - cord / 2, z), Color3.new(0, 0, 0), M.SmoothPlastic, {CanCollide = false})
	P(Vector3.new(0.6, 1.8, 1.8), CFrame.new(x, ceilY - cord - 0.3, z) * CFrame.Angles(0, 0, rad(90)), C.metal, M.Metal, {Shape = CYL, CanCollide = false})
	local bulb = P(Vector3.new(0.6, 0.6, 0.6), CFrame.new(x, ceilY - cord - 0.75, z), BULB_ON, M.Neon, {Shape = BALL, CanCollide = false, Name = "Bulb"})
	local l = Instance.new("PointLight")
	l.Color, l.Brightness, l.Range, l.Shadows = LAMP_COLOR, (bright or 1) * LAMP_BRIGHTNESS, LAMP_RANGE, true
	l.Parent = bulb
	table.insert(lamps, {light = l, bulb = bulb, on = l.Brightness})
end
local function flame(cf, size, color)
	local base = P(size, cf, color or Color3.fromRGB(255, 140, 50), M.Neon, {CanCollide = false, Name = "Flame", Transparency = 0.2})
	local l = Instance.new("PointLight")
	l.Color, l.Brightness, l.Range, l.Shadows = Color3.fromRGB(255, 150, 70), FIRE_BRIGHTNESS, 18, true
	l.Parent = base
	local f = Instance.new("Fire")
	f.Size, f.Heat, f.Parent = size.X * 1.5, 6, base
	table.insert(flames, {light = l, base = base})
end
lamp(-8, L1 - T, -2, 1.5, 0.6); lamp(9, L1 - T, 5, 1.5, 0.6)
lamp(0, L2 - T, -7); lamp(-6, L2 - T, 9); lamp(9, L2 - T, 9)
lamp(-10, L3 - T, -5); lamp(8, L3 - T, -5); lamp(6, L3 - T, 10)
lamp(2, TOP + R - 0.5, 0, 2.5, 0.8)
flame(CFrame.new(-6, L1 + 0.7, 13.4), Vector3.new(2.2, 0.6, 0.8))
for _, p in ipairs({Vector3.new(-6, L3 + 0.6, -12), Vector3.new(12, L3 + 0.6, 9)}) do
	P(Vector3.new(0.4, 1, 0.4), CFrame.new(p), Color3.fromRGB(230, 220, 200), M.SmoothPlastic, {CanCollide = false})
	flame(CFrame.new(p + Vector3.new(0, 0.65, 0)), Vector3.new(0.2, 0.3, 0.2))
end
do
	local l = Instance.new("PointLight")
	l.Color, l.Brightness, l.Range = Color3.fromRGB(255, 100, 40), 1.5, 14
	l.Parent = house:FindFirstChild("BoilerGlow")
end

---------------------------------------------------------------- МЁРТВЫЕ ДЕРЕВЬЯ
for i = 1, 10 do
	local ang = i / 10 * math.pi * 2 + rng:NextNumber(-0.2, 0.2)
	local dist = rng:NextNumber(45, 80)
	local base = Vector3.new(math.cos(ang) * dist, 0, math.sin(ang) * dist)
	local hgt = rng:NextNumber(14, 22)
	P(Vector3.new(1.4, hgt, 1.4), CFrame.new(base + Vector3.new(0, hgt / 2, 0)) * CFrame.Angles(rng:NextNumber(-0.08, 0.08), 0, rng:NextNumber(-0.08, 0.08)), C.darkWood, M.Wood, {Name = "Tree"})
	for b = 1, 3 do
		P(Vector3.new(0.5, rng:NextNumber(4, 7), 0.5), CFrame.new(base + Vector3.new(0, hgt * (0.5 + b * 0.12), 0)) * CFrame.Angles(0, rng:NextNumber(0, 6.28), rad(rng:NextNumber(35, 60))) * CFrame.new(0, 2.5, 0), C.darkWood, M.Wood)
	end
end

---------------------------------------------------------------- СПАВН
for _, s in ipairs(workspace:GetDescendants()) do
	if s:IsA("SpawnLocation") then s.Enabled = false end
end
local spawn = Instance.new("SpawnLocation")
spawn.Name, spawn.Size, spawn.Anchored, spawn.Transparency = "HouseSpawn", Vector3.new(6, 0.2, 6), true, 1
spawn.CanCollide, spawn.Neutral, spawn.Duration = false, true, 0
spawn.CFrame = CFrame.lookAt(Vector3.new(0, 0.1, -42), Vector3.new(0, 0.1, 0))
spawn.Parent = house
local decal = spawn:FindFirstChildOfClass("Decal")
if decal then decal:Destroy() end

---------------------------------------------------------------- ОСВЕЩЕНИЕ
for _, e in ipairs(Lighting:GetChildren()) do
	if e:IsA("PostEffect") or e:IsA("Atmosphere") then e:Destroy() end
end
Lighting.ClockTime, Lighting.Brightness = 0, 2
Lighting.Ambient, Lighting.OutdoorAmbient, Lighting.ExposureCompensation = AMBIENT, OUTDOOR, EXPOSURE
Lighting.EnvironmentDiffuseScale, Lighting.EnvironmentSpecularScale, Lighting.GlobalShadows = 0.4, 0.4, true
local atm = Instance.new("Atmosphere")
atm.Density, atm.Color, atm.Decay, atm.Haze = FOG, Color3.fromRGB(60, 65, 80), Color3.fromRGB(35, 35, 45), 1
atm.Parent = Lighting
local cc = Instance.new("ColorCorrectionEffect")
cc.Brightness, cc.Contrast, cc.Saturation, cc.Parent = 0.05, 0.1, -0.15, Lighting
local bloom = Instance.new("BloomEffect")
bloom.Intensity, bloom.Size, bloom.Threshold, bloom.Parent = 0.8, 28, 1.3, Lighting

if SOUND_AMBIENT ~= "" then
	local s = Instance.new("Sound")
	s.SoundId, s.Looped, s.Volume, s.Parent = SOUND_AMBIENT, true, 0.4, workspace
	s:Play()
end

---------------------------------------------------------------- МИГАНИЕ
local function setLamp(l, on)
	l.light.Brightness = on and l.on or 0
	l.bulb.Color = on and BULB_ON or BULB_OFF
end
if FLICKER then
	task.spawn(function()
		while true do
			task.wait(rng:NextNumber(6, 15))
			local l = lamps[rng:NextInteger(1, #lamps)]
			for _ = 1, rng:NextInteger(1, 3) do
				setLamp(l, false); task.wait(rng:NextNumber(0.05, 0.25))
				setLamp(l, true); task.wait(rng:NextNumber(0.05, 0.2))
			end
		end
	end)
end
task.spawn(function()
	while true do
		for _, f in ipairs(flames) do f.light.Brightness = FIRE_BRIGHTNESS * rng:NextNumber(0.75, 1.15) end
		task.wait(0.1)
	end
end)
]]></ProtectedString></Properties></Item><Item class="Script" referent="RBX5"><Properties><string name="Name">Items</string><bool name="Disabled">false</bool><token name="RunContext">0</token><ProtectedString name="Source"><![CDATA[--[[
	ПРЕДМЕТЫ (Script → ServerScriptService, имя "Items")
	Батарейки, записки, ключ, амулет + сундук в подвале.
	Модели кладутся в ReplicatedStorage.ItemModels — клиент берёт их для рук и иконок.
]]
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")

local MAX_SLOTS = 5
local BY, L1, L2, L3 = -9.2, 1.4, 12, 22.6
local M = Enum.Material
local CYL, BALL = Enum.PartType.Cylinder, Enum.PartType.Ball
local GOLD = Color3.fromRGB(200, 160, 60)
local BRASS = Color3.fromRGB(170, 130, 60)

local remote = ReplicatedStorage:FindFirstChild("ItemRemote") or Instance.new("RemoteEvent")
remote.Name = "ItemRemote"
remote.Parent = ReplicatedStorage
workspace:SetAttribute("MonstersGone", false)

for _, n in ipairs({"ItemModels"}) do
	local o = ReplicatedStorage:FindFirstChild(n)
	if o then o:Destroy() end
end
local templates = Instance.new("Folder")
templates.Name = "ItemModels"
templates.Parent = ReplicatedStorage
local oldF = workspace:FindFirstChild("Items")
if oldF then oldF:Destroy() end
local folder = Instance.new("Folder")
folder.Name = "Items"
folder.Parent = workspace

---------------------------------------------------------------- КОНСТРУКТОР МОДЕЛЕЙ
-- деталь модели; cf — относительно центра модели; приваривается к Handle
local function part(model, size, cf, color, mat, shape, props)
	local p = Instance.new(props and props.Class or "Part")
	if shape then p.Shape = shape end
	p.Size, p.CFrame, p.Color, p.Material = size, cf, color, mat or M.SmoothPlastic
	p.Anchored, p.CanCollide, p.Massless = true, false, true
	p.TopSurface, p.BottomSurface = Enum.SurfaceType.Smooth, Enum.SurfaceType.Smooth
	if props then for k, v in pairs(props) do if k ~= "Class" then p[k] = v end end end
	p.Parent = model
	local h = model.PrimaryPart
	if h and h ~= p then
		local w = Instance.new("WeldConstraint")
		w.Part0, w.Part1, w.Parent = h, p, p
	end
	return p
end
local function newModel(id, handleSize)
	local m = Instance.new("Model")
	m.Name = id
	local h = Instance.new("Part")
	h.Name, h.Size, h.Transparency, h.Anchored, h.CanCollide = "Handle", handleSize, 1, true, true
	h.CFrame = CFrame.new()
	h.Parent = m
	m.PrimaryPart = h
	m.Parent = templates
	return m
end
local UP = CFrame.Angles(0, 0, math.rad(90)) -- цилиндр стоймя

-- БАТАРЕЙКА: чёрный корпус, золотой верх, этикетка, клемма, индикатор
do
	local m = newModel("battery", Vector3.new(0.5, 1.25, 0.5))
	part(m, Vector3.new(0.85, 0.48, 0.48), CFrame.new(0, -0.12, 0) * UP, Color3.fromRGB(25, 25, 28), M.SmoothPlastic, CYL)
	part(m, Vector3.new(0.3, 0.485, 0.485), CFrame.new(0, 0.42, 0) * UP, GOLD, M.Metal, CYL)
	part(m, Vector3.new(0.35, 0.495, 0.495), CFrame.new(0, -0.05, 0) * UP, Color3.fromRGB(220, 120, 30), M.SmoothPlastic, CYL)
	part(m, Vector3.new(0.08, 0.5, 0.5), CFrame.new(0, 0.15, 0) * UP, Color3.fromRGB(80, 200, 90), M.Neon, CYL, {Transparency = 0.3})
	part(m, Vector3.new(0.1, 0.2, 0.2), CFrame.new(0, 0.6, 0) * UP, Color3.fromRGB(190, 190, 195), M.Metal, CYL)
	part(m, Vector3.new(0.04, 0.42, 0.42), CFrame.new(0, -0.56, 0) * UP, Color3.fromRGB(150, 150, 155), M.Metal, CYL)
	local l = Instance.new("PointLight")
	l.Color, l.Brightness, l.Range, l.Parent = Color3.fromRGB(90, 255, 110), 0.4, 3, m.PrimaryPart
end

-- ЗАПИСКА: мятая бумага, строчки, загнутый уголок, кровавый отпечаток
do
	local m = newModel("note", Vector3.new(1.4, 0.06, 1.8))
	local paper = part(m, Vector3.new(1.4, 0.04, 1.8), CFrame.Angles(0, 0, math.rad(1.5)), Color3.fromRGB(225, 215, 185), M.SmoothPlastic)
	local gui = Instance.new("SurfaceGui")
	gui.Face, gui.CanvasSize, gui.LightInfluence, gui.Parent = Enum.NormalId.Top, Vector2.new(140, 180), 1, paper
	for i = 1, 9 do
		local line = Instance.new("Frame")
		line.BorderSizePixel, line.BackgroundColor3 = 0, Color3.fromRGB(40, 35, 60)
		line.Size = UDim2.fromOffset(math.random(60, 110), 2)
		line.Position = UDim2.fromOffset(14, 14 + i * 16)
		line.Rotation = math.random(-3, 3)
		line.Parent = gui
	end
	part(m, Vector3.new(0.04, 0.3, 0.3), CFrame.new(0.55, 0.03, 0.75) * CFrame.Angles(0, math.rad(-45), math.rad(90)), Color3.fromRGB(200, 190, 160), M.SmoothPlastic, nil, {Class = "WedgePart"})
	part(m, Vector3.new(0.02, 0.38, 0.32), CFrame.new(-0.35, 0.03, -0.5) * CFrame.Angles(0, 0.4, math.rad(90)), Color3.fromRGB(110, 10, 10), M.SmoothPlastic, CYL, {Transparency = 0.15})
	part(m, Vector3.new(0.02, 0.12, 0.12), CFrame.new(-0.1, 0.03, -0.25) * CFrame.Angles(0, 0, math.rad(90)), Color3.fromRGB(110, 10, 10), M.SmoothPlastic, CYL)
	part(m, Vector3.new(1.3, 0.045, 0.03), CFrame.new(0, 0.005, 0.1), Color3.fromRGB(200, 190, 160), M.SmoothPlastic) -- сгиб
end

-- КЛЮЧ: кольцо, стержень, бородка, бирка
do
	local m = newModel("key", Vector3.new(0.6, 0.15, 1.7))
	for i = 0, 9 do
		local a = i / 10 * math.pi * 2
		part(m, Vector3.new(0.09, 0.09, 0.17), CFrame.new(math.cos(a) * 0.24, 0, 0.55 + math.sin(a) * 0.24) * CFrame.Angles(0, -a, 0), BRASS, M.Metal)
	end
	part(m, Vector3.new(1.0, 0.09, 0.09), CFrame.new(0, 0, -0.15) * CFrame.Angles(0, math.rad(90), 0), BRASS, M.Metal, CYL)
	part(m, Vector3.new(0.18, 0.18, 0.18), CFrame.new(0, 0, 0.3), BRASS, M.Metal, BALL)
	part(m, Vector3.new(0.28, 0.06, 0.12), CFrame.new(0.12, 0, -0.55), BRASS, M.Metal)
	part(m, Vector3.new(0.2, 0.06, 0.1), CFrame.new(0.1, 0, -0.38), BRASS, M.Metal)
	part(m, Vector3.new(0.3, 0.03, 0.4), CFrame.new(-0.25, 0, 0.95) * CFrame.Angles(0, 0.5, 0), Color3.fromRGB(150, 30, 25), M.Fabric)
end

-- АМУЛЕТ: золотая оправа, светящийся камень, цепочка
do
	local m = newModel("amulet", Vector3.new(0.9, 0.25, 1.6))
	part(m, Vector3.new(0.1, 0.6, 0.6), CFrame.new(0, -0.04, 0) * UP, GOLD, M.Metal, CYL)
	for i = 0, 11 do
		local a = i / 12 * math.pi * 2
		part(m, Vector3.new(0.12, 0.12, 0.14), CFrame.new(math.cos(a) * 0.33, 0.02, math.sin(a) * 0.33) * CFrame.Angles(0, -a, 0), GOLD, M.Metal)
	end
	local gem = part(m, Vector3.new(0.36, 0.36, 0.36), CFrame.new(0, 0.06, 0), Color3.fromRGB(230, 20, 40), M.Neon, BALL)
	for i = 1, 9 do
		part(m, Vector3.new(0.07, 0.07, 0.07), CFrame.new(math.sin(i * 0.7) * 0.12, 0, -0.38 - i * 0.09), GOLD, M.Metal, BALL)
	end
	local l = Instance.new("PointLight")
	l.Color, l.Brightness, l.Range, l.Parent = Color3.fromRGB(255, 40, 60), 1.2, 7, gem
end

---------------------------------------------------------------- ДАННЫЕ
local ITEMS = {
	battery = "Батарейка", note = "Записка", key = "Ключ", amulet = "Амулет",
}
local NOTES = {
	"Они пришли, когда мы открыли подвал. Высокий бродит по комнатам. Пока ты СМОТРИШЬ на него — он не может двинуться. Не отводи взгляд.",
	"Тот, что внизу, ползает в темноте. Он боится света. Свети ему в лицо фонариком — и он замрёт. Береги батарейки.",
	"Ключ от сундука я спрятал на чердаке. В сундуке, в подвале, лежит амулет. Только он прогонит их.",
	"Если ты это читаешь — беги. Когда он тебя схватит, он берёт тебя в руки... Я слышал, как это было с Томом.",
}
local function makeData(id, text) return {id = id, name = ITEMS[id], text = text} end

local inventories = {}
local function send(player) remote:FireClient(player, "inv", inventories[player] or {}) end
local function msg(player, text) remote:FireClient(player, "msg", text) end
Players.PlayerAdded:Connect(function(p) inventories[p] = {} end)
Players.PlayerRemoving:Connect(function(p) inventories[p] = nil end)
for _, p in ipairs(Players:GetPlayers()) do inventories[p] = {} end

local function spawnItem(data, cf, anchored)
	local m = templates[data.id]:Clone()
	m:PivotTo(cf)
	for _, p in ipairs(m:GetDescendants()) do
		if p:IsA("BasePart") then p.Anchored = anchored and true or false end
	end
	m.Parent = folder
	local prompt = Instance.new("ProximityPrompt")
	prompt.ActionText, prompt.ObjectText = "Подобрать", data.name
	prompt.MaxActivationDistance, prompt.RequiresLineOfSight = 7, false
	prompt.Parent = m.PrimaryPart
	prompt.Triggered:Connect(function(player)
		local inv = inventories[player]
		if not inv or not m.Parent then return end
		if #inv >= MAX_SLOTS then msg(player, "Карманы полны. Выбрось что-нибудь (G)"); return end
		m:Destroy()
		table.insert(inv, data)
		send(player)
		if data.id == "amulet" then
			workspace:SetAttribute("MonstersGone", true)
			remote:FireClient(player, "win")
		end
	end)
	return m
end

remote.OnServerEvent:Connect(function(player, action, slot)
	local inv = inventories[player]
	if not inv then return end
	if action == "sync" then send(player); return end
	if typeof(slot) ~= "number" or not inv[slot] then return end
	local item = inv[slot]
	if action == "use" and item.id == "battery" then
		table.remove(inv, slot)
		send(player)
		remote:FireClient(player, "battery")
	elseif action == "drop" then
		local hrp = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
		if not hrp then return end
		table.remove(inv, slot)
		send(player)
		local params = RaycastParams.new()
		params.FilterDescendantsInstances = {player.Character}
		local look = hrp.CFrame.LookVector
		local hit = workspace:Raycast(hrp.Position, look * 2.5, params)
		local pos = hit and (hit.Position - look * 0.6) or (hrp.Position + look * 2.5)
		spawnItem(item, CFrame.new(pos) * CFrame.Angles(0, math.random() * 6.28, 0), false)
	end
end)

---------------------------------------------------------------- РАССТАНОВКА
local function at(x, y, z, yaw) return CFrame.new(x, y, z) * CFrame.Angles(0, math.rad(yaw or math.random(0, 360)), 0) end
spawnItem(makeData("battery"), at(-7, L1 + 2.2, -8), true)       -- столик в гостиной
spawnItem(makeData("battery"), at(9, BY + 3.8, -6), true)        -- стол в подвале
spawnItem(makeData("battery"), at(8, L2 + 3.8, -13), true)       -- письменный стол
spawnItem(makeData("battery"), at(4, L3 + 0.65, 2), true)         -- чердак
spawnItem(makeData("note", NOTES[1]), at(4, L1 + 3.25, -6), true)
spawnItem(makeData("note", NOTES[2]), at(11, BY + 3.25, -5.5), true)
spawnItem(makeData("note", NOTES[3]), at(-9, L2 + 2.65, -10), true)
spawnItem(makeData("note", NOTES[4]), at(-6, L3 + 0.05, -13), true)
spawnItem(makeData("key"), at(13, L3 + 0.1, -13.5), true)

---------------------------------------------------------------- СУНДУК В ПОДВАЛЕ
local chestModel = Instance.new("Model")
chestModel.Name = "Chest"
chestModel.Parent = folder
local chestCF = CFrame.new(-3, BY, 12.5) -- перёд смотрит на -Z
local WOOD, IRON = Color3.fromRGB(80, 50, 30), Color3.fromRGB(45, 45, 48)
local function cpart(size, cf, color, mat)
	local p = Instance.new("Part")
	p.Size, p.CFrame, p.Color, p.Material, p.Anchored = size, chestCF * cf, color, mat, true
	p.Parent = chestModel
	return p
end
cpart(Vector3.new(3, 0.2, 2), CFrame.new(0, 0.1, 0), WOOD, M.WoodPlanks)
local front = cpart(Vector3.new(3, 1.4, 0.15), CFrame.new(0, 0.7, -0.93), WOOD, M.WoodPlanks)
cpart(Vector3.new(3, 1.4, 0.15), CFrame.new(0, 0.7, 0.93), WOOD, M.WoodPlanks)
cpart(Vector3.new(0.15, 1.4, 2), CFrame.new(-1.43, 0.7, 0), WOOD, M.WoodPlanks)
cpart(Vector3.new(0.15, 1.4, 2), CFrame.new(1.43, 0.7, 0), WOOD, M.WoodPlanks)
for _, x in ipairs({-1, 1}) do cpart(Vector3.new(0.2, 1.42, 2.05), CFrame.new(x, 0.7, 0), IRON, M.Metal) end
cpart(Vector3.new(0.4, 0.5, 0.15), CFrame.new(0, 1.05, -1.02), BRASS, M.Metal).Name = "Lock"

local hinge = chestCF * CFrame.new(0, 1.4, 0.95)
local lid = Instance.new("Part")
lid.Size, lid.Color, lid.Material, lid.Anchored = Vector3.new(3.05, 0.3, 2), WOOD, M.WoodPlanks, true
lid.CFrame = hinge * CFrame.new(0, 0.15, -1)
lid.Parent = chestModel
for _, x in ipairs({-1, 1}) do
	local b = Instance.new("Part")
	b.Size, b.Color, b.Material, b.CanCollide, b.Massless = Vector3.new(0.2, 0.34, 2.05), IRON, M.Metal, false, true
	b.CFrame = lid.CFrame * CFrame.new(x, 0, 0)
	b.Parent = chestModel
	local w = Instance.new("WeldConstraint")
	w.Part0, w.Part1, w.Parent = lid, b, b
end

local prompt = Instance.new("ProximityPrompt")
prompt.ActionText, prompt.ObjectText = "Открыть", "Сундук"
prompt.MaxActivationDistance, prompt.RequiresLineOfSight = 7, false
prompt.Parent = front
local opened = false
prompt.Triggered:Connect(function(player)
	if opened then return end
	local inv = inventories[player]
	local keySlot
	for i, it in ipairs(inv or {}) do if it.id == "key" then keySlot = i end end
	if not keySlot then msg(player, "Заперто. Нужен ключ."); return end
	opened = true
	prompt.Enabled = false
	table.remove(inv, keySlot)
	send(player)
	msg(player, "Ключ подошёл…")
	TweenService:Create(lid, TweenInfo.new(1.2, Enum.EasingStyle.Back, Enum.EasingDirection.Out), {CFrame = hinge * CFrame.Angles(math.rad(100), 0, 0) * CFrame.new(0, 0.15, -1)}):Play()
	task.wait(0.6)
	spawnItem(makeData("amulet"), chestCF * CFrame.new(0, 0.35, 0), true)
end)
]]></ProtectedString></Properties></Item><Item class="Script" referent="RBX6"><Properties><string name="Name">Monsters</string><bool name="Disabled">false</bool><token name="RunContext">0</token><ProtectedString name="Source"><![CDATA[--[[
	МОНСТРЫ (Script → ServerScriptService, имя "Monsters")
	Наблюдатель ходит по 1–3 этажу и не может двигаться, пока ты на него СМОТРИШЬ.
	Ползун сидит в подвале и замирает, когда на него СВЕТИТ фонарик.
	Поймает: берёт в руки, поднимает, отрывает голову, тело падает.
]]
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local PathfindingService = game:GetService("PathfindingService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")
local Debris = game:GetService("Debris")

local remote = ReplicatedStorage:FindFirstChild("ItemRemote") or Instance.new("RemoteEvent")
remote.Name = "ItemRemote"
remote.Parent = ReplicatedStorage
local BY, L1, L2 = -9.2, 1.4, 12
local CATCH_DIST = 3.6
local SIGHT_MEMORY = 0.35 -- сколько секунд действует «на меня смотрят/светят»
local M = Enum.Material
local BALL = Enum.PartType.Ball
local rng = Random.new()
local sin, cos, abs, max, min, clamp = math.sin, math.cos, math.abs, math.max, math.min, math.clamp

local oldF = workspace:FindFirstChild("Monsters")
if oldF then oldF:Destroy() end
local folder = Instance.new("Folder")
folder.Name = "Monsters"
folder.Parent = workspace

local DEFS = {
	Watcher = { -- высокий, бледный, длинные руки
		speed = 13, freq = 1.5, home = Vector3.new(8, L2, 6), agentH = 5,
		skin = Color3.fromRGB(200, 195, 185), dark = Color3.fromRGB(55, 45, 45), eye = Color3.fromRGB(255, 20, 20),
		tL = 2.3, sL = 2.3, legW = 0.5, torsoL = 2.8, torsoW = 1.6, torsoD = 0.8, shW = 1.0,
		neckL = 0.7, head = Vector3.new(1, 1.35, 1.1), uL = 2.5, fL = 2.4, armW = 0.36,
		lean = 0.12, knee = 0.15, walkLean = 0.15, armSwing = 0.35, grabLean = 0.25, holdDist = 3.1, lift = 2.2,
	},
	Crawler = { -- сгорбленный, тёмный, ходит на четвереньках
		speed = 11, freq = 2.4, home = Vector3.new(-6, BY, 6), agentH = 4.5,
		skin = Color3.fromRGB(45, 40, 38), dark = Color3.fromRGB(18, 15, 15), eye = Color3.fromRGB(220, 255, 80),
		tL = 1.4, sL = 1.5, legW = 0.6, torsoL = 2.8, torsoW = 1.5, torsoD = 1.1, shW = 0.9,
		neckL = 0.6, head = Vector3.new(1.1, 1, 1.3), uL = 2.0, fL = 2.1, armW = 0.42,
		lean = 1.15, knee = 0.9, walkLean = 0.05, armSwing = 0.5, grabLean = 0.3, holdDist = 2.4, lift = 1.2,
	},
}
local WANDER = {
	Watcher = {
		Vector3.new(-8, L1, -6), Vector3.new(6, L1, -9), Vector3.new(8, L1, 5), Vector3.new(-6, L1, 8),
		Vector3.new(-8, L2, 0), Vector3.new(6, L2, -8), Vector3.new(6, L2, 8), Vector3.new(-6, L2, 6),
		Vector3.new(0, 22.6, 0), Vector3.new(8, 22.6, 2), Vector3.new(-8, 22.6, -4),
	},
	Crawler = {
		Vector3.new(-6, BY, 6), Vector3.new(6, BY, -2), Vector3.new(-10, BY, -6), Vector3.new(0, BY, 6), Vector3.new(12, BY, 2),
	},
}

---------------------------------------------------------------- МОДЕЛЬ
local function build(name, d)
	local model = Instance.new("Model")
	model.Name = name
	local rig = {}
	local function add(joint, size, offset, color, mat, shape)
		local p = Instance.new("Part")
		p.Size, p.Color, p.Material = size, color or d.skin, mat or M.SmoothPlastic
		if shape then p.Shape = shape end
		p.Anchored, p.CanCollide, p.CanTouch = true, false, false
		p.TopSurface, p.BottomSurface = Enum.SurfaceType.Smooth, Enum.SurfaceType.Smooth
		p.Parent = model
		table.insert(rig, {p, joint, offset})
		return p
	end
	local k, h = d.dark, d.head
	add("pelvis", Vector3.new(d.torsoW * 0.9, 0.6, d.torsoD), CFrame.new(), k, M.Fabric)
	local torso = add("torso", Vector3.new(d.torsoW, d.torsoL, d.torsoD), CFrame.new(0, d.torsoL / 2, 0))
	torso.Name = "Torso"
	for i = 1, 5 do add("torso", Vector3.new(0.28, 0.28, 0.28), CFrame.new(0, i * d.torsoL / 6, d.torsoD / 2), k, nil, BALL) end
	for i = 1, 3 do add("torso", Vector3.new(d.torsoW * 0.8, 0.12, 0.12), CFrame.new(0, d.torsoL * (0.4 + i * 0.13), -d.torsoD / 2), k) end
	add("neck", Vector3.new(0.35, d.neckL + 0.3, 0.35), CFrame.new(0, d.neckL / 2, 0))
	local head = add("head", h, CFrame.new(0, h.Y / 2, 0))
	head.Name = "Head"
	add("head", Vector3.new(h.X * 1.02, 0.18, 0.25), CFrame.new(0, h.Y * 0.7, -h.Z / 2 + 0.05), k)
	for _, sx in ipairs({-1, 1}) do
		add("head", Vector3.new(0.34, 0.3, 0.1), CFrame.new(sx * h.X * 0.24, h.Y * 0.56, -h.Z / 2 - 0.01), Color3.new(0, 0, 0))
		add("head", Vector3.new(0.16, 0.16, 0.16), CFrame.new(sx * h.X * 0.24, h.Y * 0.56, -h.Z / 2 - 0.06), d.eye, M.Neon, BALL)
	end
	add("head", Vector3.new(h.X * 0.7, 0.14, 0.1), CFrame.new(0, h.Y * 0.2, -h.Z / 2 - 0.01), Color3.fromRGB(40, 0, 0))
	add("jaw", Vector3.new(h.X * 0.85, 0.3, h.Z * 0.75), CFrame.new(0, -0.15, -h.Z * 0.3))
	for i = -2, 2 do add("jaw", Vector3.new(0.08, 0.22, 0.08), CFrame.new(i * 0.13, 0.1, -h.Z * 0.64), Color3.fromRGB(230, 225, 200)) end
	for _, s in ipairs({"L", "R"}) do
		add("sh" .. s, Vector3.new(d.armW, d.uL, d.armW), CFrame.new(0, -d.uL / 2, 0))
		add("el" .. s, Vector3.new(d.armW, d.armW, d.armW) * 1.25, CFrame.new(), nil, nil, BALL)
		add("el" .. s, Vector3.new(d.armW * 0.85, d.fL, d.armW * 0.85), CFrame.new(0, -d.fL / 2, 0))
		local hand = add("hand" .. s, Vector3.new(d.armW * 1.5, 0.45, 0.25), CFrame.new(0, -0.2, 0))
		hand.Name = "Hand" .. s
		for f = -1, 1 do add("hand" .. s, Vector3.new(0.08, 0.9, 0.08), CFrame.new(f * 0.17, -0.75, -0.05) * CFrame.Angles(0.25, 0, f * 0.12), k) end
		add("hip" .. s, Vector3.new(d.legW, d.tL, d.legW), CFrame.new(0, -d.tL / 2, 0), k, M.Fabric)
		add("kn" .. s, Vector3.new(d.legW * 0.8, d.sL, d.legW * 0.8), CFrame.new(0, -d.sL / 2, 0))
		add("ft" .. s, Vector3.new(d.legW * 1.1, 0.3, 1.1), CFrame.new(0, -0.15, -0.3))
	end
	local light = Instance.new("PointLight")
	light.Color, light.Brightness, light.Range = d.eye, 1.2, 7
	light.Parent = head
	model.PrimaryPart = torso
	model.Parent = folder
	return model, rig
end

---------------------------------------------------------------- АНИМАЦИЯ (кости считаются вручную)
local function joints(m)
	local d, c = m.def, m.cur
	local hipH = (d.tL + d.sL) * cos(d.knee / 2) + 0.3
	local root = CFrame.new(m.pos) * CFrame.Angles(0, m.yaw, 0)
	local J = {}
	J.pelvis = root * CFrame.new(0, hipH + c.hipY, 0) * CFrame.Angles(0, c.twist * 0.4, 0)
	J.torso = J.pelvis * CFrame.new(0, 0.2, 0) * CFrame.Angles(-c.lean, c.twist, c.sway)
	local top = J.torso * CFrame.new(0, d.torsoL, 0)
	J.neck = top * CFrame.Angles(c.lean * 0.8 + c.headP * 0.4, c.headY * 0.3, 0)
	J.head = J.neck * CFrame.new(0, d.neckL, 0) * CFrame.Angles(c.lean * 0.2 + c.headP * 0.6, c.headY * 0.7, c.headR)
	J.jaw = J.head * CFrame.new(0, d.head.Y * 0.22, -d.head.Z * 0.15) * CFrame.Angles(-c.jaw, 0, 0)
	for _, s in ipairs({"L", "R"}) do
		local sx = s == "L" and -1 or 1
		J["sh" .. s] = top * CFrame.new(sx * d.shW, -0.25, 0) * CFrame.Angles(c.lean + c[s .. "armP"], 0, sx * c[s .. "armO"])
		J["el" .. s] = J["sh" .. s] * CFrame.new(0, -d.uL, 0) * CFrame.Angles(c[s .. "elb"], 0, 0)
		J["hand" .. s] = J["el" .. s] * CFrame.new(0, -d.fL, 0) * CFrame.Angles(c[s .. "elb"] * 0.4, c[s .. "wrist"], 0)
		local legP, knee = c[s .. "leg"], c[s .. "knee"]
		J["hip" .. s] = J.pelvis * CFrame.new(sx * d.torsoW * 0.3, 0, 0) * CFrame.Angles(legP, 0, sx * 0.05)
		J["kn" .. s] = J["hip" .. s] * CFrame.new(0, -d.tL, 0) * CFrame.Angles(-knee, 0, 0)
		J["ft" .. s] = J["kn" .. s] * CFrame.new(0, -d.sL, 0) * CFrame.Angles(knee - legP, 0, 0)
	end
	return J
end

local function apply(m)
	local J = joints(m)
	local parts, cfs = m.parts, m.cfs
	for i, r in ipairs(m.rig) do cfs[i] = J[r[2]] * r[3] end
	workspace:BulkMoveTo(parts, cfs, Enum.BulkMoveMode.FireCFrameChanged)
	m.J = J
end

-- целевая поза: idle/ходьба/хватание; плавно смешивается с текущей
local function animate(m, dt, t, moving)
	local d, g = m.def, {}
	local crawler = m.name == "Crawler"
	m.walk += ((moving and 1 or 0) - m.walk) * min(1, dt * 6)
	local w = m.walk
	if moving then m.phase += dt * d.freq * math.pi * 2 * (m.hunting and 1 or 0.6) end
	local ph = m.phase
	local breath = sin(t * 1.8)
	if t > m.nextTwitch then -- резкие подёргивания головой
		m.nextTwitch = t + rng:NextNumber(crawler and 0.3 or 1.5, crawler and 1.4 or 4)
		m.twitch = Vector3.new(rng:NextNumber(-0.5, 0.5), rng:NextNumber(-0.9, 0.9), rng:NextNumber(-0.6, 0.6))
	end
	m.twitch *= 0.93
	g.lean = d.lean + d.walkLean * w + breath * 0.02
	g.twist = sin(ph) * 0.15 * w
	g.sway = sin(t * 1.1) * 0.04 + sin(ph) * 0.05 * w
	g.hipY = (abs(cos(ph)) - 1) * 0.18 * w + breath * 0.04
	g.headP = m.twitch.X + (crawler and 0.15 or -0.1)
	g.headY = sin(t * 0.45) * 0.7 * (1 - w) + m.twitch.Y
	g.headR = sin(t * 0.37) * 0.35 + m.twitch.Z + sin(ph * 2) * 0.08 * w
	g.jaw = crawler and (0.2 + abs(sin(t * 9)) * 0.25) or (0.08 + max(0, breath) * 0.12 + 0.2 * w)
	local swing = sin(ph) * d.armSwing * w
	for _, s in ipairs({"L", "R"}) do
		local sgn = s == "L" and 1 or -1
		g[s .. "armP"] = -swing * sgn + sin(t * 0.9 + sgn) * 0.05
		g[s .. "armO"] = crawler and 0.22 or 0.1 + breath * 0.02
		g[s .. "elb"] = crawler and 0.35 + max(0, swing * sgn) * 0.6 or 0.25 + max(0, -swing * sgn) * 0.5
		g[s .. "wrist"] = sin(t * 2 + sgn) * 0.2
		g[s .. "leg"] = d.knee / 2 + sin(ph) * 0.55 * w * sgn
		g[s .. "knee"] = d.knee + max(0, cos(ph) * sgn) * 0.9 * w
	end
	if m.grabT then -- ХВАТАНИЕ
		local gt = m.grabT
		local lift = clamp((gt - 0.5) / 1.1, 0, 1)
		local shake = (gt > 0.5 and gt < 2.2) and rng:NextNumber(-0.08, 0.08) or 0
		g.lean, g.twist, g.sway = d.grabLean, shake, shake
		g.hipY = crawler and 0.6 or 0
		g.headP, g.headY = gt > 2.2 and 0.45 or -0.15, gt > 2.2 and 0.35 or 0
		g.headR = 0.3 + shake * 2
		g.jaw = gt > 2.2 and 0.95 or 0.35 + abs(sin(gt * 18)) * 0.25
		for _, s in ipairs({"L", "R"}) do
			g[s .. "armP"] = 1.2 + lift * 0.35 + shake
			g[s .. "armO"] = -0.18
			g[s .. "elb"] = 0.7 - lift * 0.2
			g[s .. "wrist"] = 0
			g[s .. "leg"], g[s .. "knee"] = d.knee / 2 + 0.1, d.knee + 0.1
		end
		if gt > 1.6 and gt < 2.2 then g.Rwrist = (gt - 1.6) * 4; g.RarmP = 1.7 end
		if gt >= 2.2 then g.RarmP, g.RarmO, g.Relb = 2.7, 0.45, 0.15 end -- поднимает голову трофеем
		if gt >= 2.9 then g.LarmP, g.Lelb = 0.4, 0.5 end
	end
	local k = min(1, dt * (m.grabT and 12 or 8))
	for key, v in pairs(g) do
		local cur = m.cur[key] or v
		m.cur[key] = cur + (v - cur) * k
	end
	apply(m)
end

---------------------------------------------------------------- МОНСТРЫ
local monsters = {}
local function spawnMonster(name)
	local d = DEFS[name]
	local model, rig = build(name, d)
	local m = {
		name = name, def = d, model = model, rig = rig, parts = {}, cfs = {}, cur = {},
		pos = d.home, yaw = rng:NextNumber(0, 6.28), walk = 0, phase = 0, nextTwitch = 0, twitch = Vector3.zero,
		lookedAt = 0, litAt = 0, alive = true,
		path = PathfindingService:CreatePath({AgentRadius = 1.6, AgentHeight = d.agentH, AgentCanJump = false, WaypointSpacing = 3}),
	}
	for i, r in ipairs(rig) do m.parts[i] = r[1] end
	animate(m, 1, 0, false)
	table.insert(monsters, m)
	return m
end
spawnMonster("Watcher")
spawnMonster("Crawler")

local function frozen(m)
	local now = os.clock()
	if m.name == "Watcher" then return now - m.lookedAt < SIGHT_MEMORY end
	return now - m.litAt < SIGHT_MEMORY
end

-- клиент каждые 0.15 с сообщает: на кого смотрю, на кого свечу
remote.OnServerEvent:Connect(function(_, action, looking, lit)
	if action ~= "sight" then return end
	local now = os.clock()
	for _, m in ipairs(monsters) do
		if type(looking) == "table" and table.find(looking, m.name) then m.lookedAt = now end
		if type(lit) == "table" and table.find(lit, m.name) then m.litAt = now end
	end
end)

local caught = {}
local function aliveHRP(p)
	local char = p.Character
	local hum = char and char:FindFirstChildOfClass("Humanoid")
	local hrp = char and char:FindFirstChild("HumanoidRootPart")
	if hum and hrp and hum.Health > 0 and not caught[p] then return hrp, hum end
	return nil
end
local function findTarget(m)
	local best, bestD
	for _, p in ipairs(Players:GetPlayers()) do
		local hrp = aliveHRP(p)
		if hrp then
			local pos = hrp.Position
			local inHouse = abs(pos.X) < 20.5 and abs(pos.Z) < 15.5
			local ok = inHouse and ((m.name == "Watcher" and pos.Y > L1 + 1) or (m.name == "Crawler" and pos.Y < L1 + 1))
			local dist = (pos - m.pos).Magnitude
			if ok and (not bestD or dist < bestD) then best, bestD = p, dist end
		end
	end
	return best
end

---------------------------------------------------------------- ПОИМКА
local function blood(parent, rate, life)
	local pe = Instance.new("ParticleEmitter")
	pe.Color = ColorSequence.new(Color3.fromRGB(150, 0, 0), Color3.fromRGB(60, 0, 0))
	pe.Size = NumberSequence.new(0.3, 0.05)
	pe.Lifetime = NumberRange.new(0.5, 1)
	pe.Speed = NumberRange.new(4, 10)
	pe.SpreadAngle = Vector2.new(35, 35)
	pe.Acceleration = Vector3.new(0, -45, 0)
	pe.Rate = rate
	pe.Parent = parent
	Debris:AddItem(pe, life)
	return pe
end

local function catch(m, player)
	local hrp, hum = aliveHRP(player)
	if not hrp then return end
	local char = player.Character
	local head = char:FindFirstChild("Head")
	local neck
	for _, j in ipairs(char:GetDescendants()) do
		if j:IsA("Motor6D") and j.Name == "Neck" then neck = j end
	end
	caught[player], m.busy, m.wps = true, true, nil
	hum.RequiresNeck = false
	hrp.Anchored = true
	local flat = Vector3.new(hrp.Position.X - m.pos.X, 0, hrp.Position.Z - m.pos.Z)
	if flat.Magnitude > 0.1 then m.yaw = math.atan2(-flat.X, -flat.Z) end
	local fwd = CFrame.Angles(0, m.yaw, 0).LookVector
	local rest = hum.RigType == Enum.HumanoidRigType.R15 and hum.HipHeight + hrp.Size.Y / 2 or 3
	local base = m.pos + fwd * m.def.holdDist
	local neckC0 = neck and neck.C0
	remote:FireClient(player, "caught", m.model)
	m.grabT = 0
	local ripped, dropped = false, false
	local t0 = os.clock()
	while true do
		local dt = RunService.Heartbeat:Wait()
		local gt = os.clock() - t0
		m.grabT = gt
		animate(m, dt, os.clock(), false)
		local lift = clamp((gt - 0.5) / 1.1, 0, 1) * m.def.lift
		local shake = (gt > 0.5 and gt < 2.2) and Vector3.new(rng:NextNumber(-1, 1), rng:NextNumber(-1, 1), 0) * 0.12 or Vector3.zero
		if hrp.Anchored then
			local p = base + Vector3.new(0, rest + lift, 0) + shake
			local look = m.J.head.Position
			hrp.CFrame = CFrame.lookAt(p, Vector3.new(look.X, p.Y, look.Z))
		end
		if neck and neck.Parent and gt > 1.6 then -- выкручивает голову
			neck.C0 = neckC0 * CFrame.Angles(0, (gt - 1.6) / 0.6 * 2.2, 0)
		end
		if gt >= 2.2 and not ripped and head then -- ОТРЫВАЕТ
			ripped = true
			if neck then neck:Destroy() end
			head.Anchored, head.CanCollide = true, false
			local torso = char:FindFirstChild("UpperTorso") or char:FindFirstChild("Torso")
			if torso then blood(torso, 250, 1.6) end
			blood(head, 60, 4)
			remote:FireClient(player, "rip")
		end
		if ripped and not dropped and head then
			local hand = m.J.handR.Position
			head.CFrame = CFrame.lookAt(hand - Vector3.new(0, 0.6, 0), m.J.head.Position)
		end
		if gt >= 2.9 and hrp.Anchored then -- тело падает на пол
			hrp.Anchored = false
			hrp.AssemblyLinearVelocity = fwd * 6
			hum.Health = 0
			local pool = Instance.new("Part")
			pool.Shape, pool.Anchored, pool.CanCollide = Enum.PartType.Cylinder, true, false
			pool.Color, pool.Material = Color3.fromRGB(70, 0, 0), M.Glass
			pool.Size = Vector3.new(0.05, 0.5, 0.5)
			pool.CFrame = CFrame.new(base + fwd * 1.5 + Vector3.new(0, 0.03, 0)) * CFrame.Angles(0, 0, math.rad(90))
			pool.Parent = workspace
			TweenService:Create(pool, TweenInfo.new(4), {Size = Vector3.new(0.05, 6, 6)}):Play()
			Debris:AddItem(pool, 60)
		end
		if gt >= 3.6 and not dropped and head then -- бросает голову
			dropped = true
			head.Anchored, head.CanCollide = false, true
			head.AssemblyLinearVelocity = fwd * 4 + Vector3.new(0, 5, 0)
			head.AssemblyAngularVelocity = Vector3.new(rng:NextNumber(-6, 6), 0, rng:NextNumber(-6, 6))
		end
		if gt >= 4.6 then break end
	end
	m.grabT, m.busy = nil, false
end

Players.PlayerAdded:Connect(function(p)
	p.CharacterAdded:Connect(function() caught[p] = nil end)
end)
for _, p in ipairs(Players:GetPlayers()) do
	p.CharacterAdded:Connect(function() caught[p] = nil end)
end
Players.PlayerRemoving:Connect(function(p) caught[p] = nil end)

---------------------------------------------------------------- МОЗГ (путь) И ДВИЖЕНИЕ
local function computePath(m, goal)
	local ok = pcall(function() m.path:ComputeAsync(m.pos + Vector3.new(0, 1, 0), goal) end)
	if ok and m.path.Status == Enum.PathStatus.Success then
		m.wps, m.wi = m.path:GetWaypoints(), 2
		return true
	end
	m.wps = nil
	return false
end

for _, m in ipairs(monsters) do
	task.spawn(function()
		while m.alive do
			local delay = 0.35
			if not m.busy then
				local target = findTarget(m)
				m.target, m.hunting = target, target ~= nil
				if target then
					local hrp = aliveHRP(target)
					if hrp then computePath(m, hrp.Position - Vector3.new(0, 2.5, 0)) end
				elseif not m.wps or m.wi > #m.wps then
					local list = WANDER[m.name]
					computePath(m, list[rng:NextInteger(1, #list)])
					delay = rng:NextNumber(1.5, 4) -- постоять перед новым маршрутом
				end
			end
			task.wait(delay)
		end
	end)
end

local function stepMonster(m, dt, t)
	if m.busy or not m.alive then return end
	if frozen(m) then return end -- статуя: ни шага, ни движения
	local speed = m.def.speed * (m.hunting and 1 or 0.45)
	local moveTo
	if m.wps and m.wi <= #m.wps then
		local wp = m.wps[m.wi].Position
		if (wp - m.pos).Magnitude < 0.7 then m.wi += 1 else moveTo = wp end
	end
	local hrp = m.target and aliveHRP(m.target)
	if hrp then
		local feet = hrp.Position - Vector3.new(0, 2.6, 0)
		local flat = Vector3.new(feet.X - m.pos.X, 0, feet.Z - m.pos.Z)
		local dy = abs(feet.Y - m.pos.Y)
		if dy < 3 and flat.Magnitude < 7 then moveTo = Vector3.new(feet.X, m.pos.Y, feet.Z) end
		if dy < 4.5 and flat.Magnitude < CATCH_DIST then
			task.spawn(catch, m, m.target)
			return
		end
	end
	local moving = false
	if moveTo then
		local delta = moveTo - m.pos
		if delta.Magnitude > 0.05 then
			m.pos += delta.Unit * min(delta.Magnitude, speed * dt)
			moving = true
			if Vector3.new(delta.X, 0, delta.Z).Magnitude > 0.1 then
				local want = math.atan2(-delta.X, -delta.Z)
				local diff = (want - m.yaw + math.pi) % (math.pi * 2) - math.pi
				m.yaw += diff * min(1, dt * 8)
			end
		end
	end
	animate(m, dt, t, moving)
end

RunService.Heartbeat:Connect(function(dt)
	local t = os.clock()
	for _, m in ipairs(monsters) do stepMonster(m, dt, t) end
end)

---------------------------------------------------------------- АМУЛЕТ ПРОГОНЯЕТ ИХ
workspace:GetAttributeChangedSignal("MonstersGone"):Connect(function()
	if not workspace:GetAttribute("MonstersGone") then return end
	for _, m in ipairs(monsters) do
		m.alive = false
		for _, p in ipairs(m.parts) do
			TweenService:Create(p, TweenInfo.new(2.5), {Transparency = 1, CFrame = p.CFrame * CFrame.new(0, -3, 0)}):Play()
		end
		Debris:AddItem(m.model, 3)
	end
end)
]]></ProtectedString></Properties></Item></Item>
<Item class="StarterPlayer" referent="RBX10"><Properties><string name="Name">StarterPlayer</string></Properties><Item class="StarterPlayerScripts" referent="RBX9"><Properties><string name="Name">StarterPlayerScripts</string></Properties><Item class="LocalScript" referent="RBX8"><Properties><string name="Name">HorrorFirstPerson</string><bool name="Disabled">false</bool><token name="RunContext">0</token><ProtectedString name="Source"><![CDATA[--[[
	ПЕРВОЕ ЛИЦО + РУКИ + ФОНАРИК + ИНВЕНТАРЬ (LocalScript → StarterPlayer → StarterPlayerScripts, имя "HorrorFirstPerson")
	E — подобрать/открыть, 1–5 — предмет в руку, ЛКМ/R — использовать, G — выбросить,
	F — фонарик, Shift — бег, C/Ctrl — присесть, Q/Z — выглянуть влево/вправо
	Наблюдателя держит твой ВЗГЛЯД, Ползуна — СВЕТ фонарика.
]]
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UIS = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PPS = game:GetService("ProximityPromptService")

local player = Players.LocalPlayer
local remote = ReplicatedStorage:WaitForChild("ItemRemote")
local models = ReplicatedStorage:WaitForChild("ItemModels")

local WALK, RUN, CROUCH = 9, 16, 5
local BASE_FOV, RUN_FOV = 70, 78
local STAMINA_MAX, DRAIN, REGEN = 100, 22, 12
local MAX_SLOTS = 5
local TORCH_BRIGHT, BATTERY_DRAIN = 3, 0.6
local TORCH_ANGLE, TORCH_RANGE = 28, 45 -- в этом конусе фонарик «держит» Ползуна
local SLEEVE, SKIN, LIGHT = Color3.fromRGB(38, 36, 34), Color3.fromRGB(234, 184, 146), Color3.fromRGB(255, 240, 215)
local SOUNDS = {heart = "", click = "", rip = "", scream = ""} -- вставь свои id звуков: "rbxassetid://..."
local sin, cos, abs, min, max, clamp, rad = math.sin, math.cos, math.abs, math.min, math.max, math.clamp, math.rad

player.CameraMode = Enum.CameraMode.LockFirstPerson
UIS.MouseIconEnabled = false

local function sound(id, vol, looped)
	if id == "" then return nil end
	local s = Instance.new("Sound")
	s.SoundId, s.Volume, s.Looped = id, vol or 0.6, looped or false
	s.Parent = workspace.CurrentCamera
	return s
end
local function playOnce(id, vol)
	local s = sound(id, vol)
	if s then s:Play(); s.Ended:Once(function() s:Destroy() end) end
end
local heartSnd = sound(SOUNDS.heart, 0, true)
if heartSnd then heartSnd:Play() end

---------------------------------------------------------------- РУКИ
local vm = Instance.new("Model")
vm.Name = "Arms"
local vmParts, skinParts = {}, {}
local function add(size, offset, color, mat, shape, group)
	local p = Instance.new("Part")
	if shape then p.Shape = shape end
	p.Size, p.Color, p.Material = size, color, mat or Enum.Material.SmoothPlastic
	p.Anchored, p.CanCollide, p.CanQuery, p.CanTouch, p.CastShadow = true, false, false, false, false
	p.Parent = vm
	table.insert(vmParts, {p, offset, group})
	return p
end
local FWD = CFrame.Angles(0, rad(90), 0)
local rArm = CFrame.new(0.7, -0.7, -0.55) * CFrame.Angles(rad(8), rad(12), 0)
local lArm = CFrame.new(-0.75, -0.8, -0.55) * CFrame.Angles(rad(12), rad(-14), 0)
add(Vector3.new(0.36, 0.36, 1.3), rArm, SLEEVE, Enum.Material.Fabric, nil, "R")
table.insert(skinParts, add(Vector3.new(0.32, 0.34, 0.36), rArm * CFrame.new(0, 0, -0.8), SKIN, nil, nil, "R"))
add(Vector3.new(0.36, 0.36, 1.3), lArm, SLEEVE, Enum.Material.Fabric, nil, "L")
table.insert(skinParts, add(Vector3.new(0.32, 0.34, 0.36), lArm * CFrame.new(0, 0, -0.8), SKIN, nil, nil, "L"))
for i = -1, 2 do -- пальцы левой руки
	table.insert(skinParts, add(Vector3.new(0.07, 0.07, 0.22), lArm * CFrame.new(i * 0.075 + 0.02, 0.12, -1.02), SKIN, nil, nil, "L"))
end
local torchCF = rArm * CFrame.new(0, 0.17, -0.85)
local DARK = Color3.fromRGB(30, 30, 30)
add(Vector3.new(0.7, 0.2, 0.2), torchCF * FWD, DARK, Enum.Material.Metal, Enum.PartType.Cylinder, "R")
add(Vector3.new(0.25, 0.28, 0.28), torchCF * CFrame.new(0, 0, -0.45) * FWD, Color3.fromRGB(40, 40, 40), Enum.Material.Metal, Enum.PartType.Cylinder, "R")
local button = add(Vector3.new(0.08, 0.05, 0.1), torchCF * CFrame.new(0, 0.11, -0.1), Color3.fromRGB(150, 30, 30), nil, nil, "R")
local lens = add(Vector3.new(0.04, 0.24, 0.24), torchCF * CFrame.new(0, 0, -0.59) * FWD, LIGHT, Enum.Material.Neon, Enum.PartType.Cylinder, "R")
local lightPart = add(Vector3.new(0.1, 0.1, 0.1), CFrame.new(0.4, -0.3, -1.3), Color3.new(), nil, nil, "R")
lightPart.Transparency = 1
local spot = Instance.new("SpotLight")
spot.Face, spot.Angle, spot.Range, spot.Brightness, spot.Shadows, spot.Color = Enum.NormalId.Front, 50, TORCH_RANGE, TORCH_BRIGHT, true, LIGHT
spot.Parent = lightPart
local glow = Instance.new("PointLight")
glow.Range, glow.Brightness, glow.Color = 5, 0.25, LIGHT
glow.Parent = lightPart

---------------------------------------------------------------- ИНТЕРФЕЙС
local gui = Instance.new("ScreenGui")
gui.Name, gui.IgnoreGuiInset, gui.ResetOnSpawn = "HorrorUI", true, false
gui.Parent = player:WaitForChild("PlayerGui")
local function frame(props, parent)
	local f = Instance.new(props.Class or "Frame")
	f.BorderSizePixel = 0
	for k, v in pairs(props) do if k ~= "Class" then f[k] = v end end
	f.Parent = parent or gui
	return f
end
local function label(props, parent)
	props.Class, props.BackgroundTransparency = "TextLabel", 1
	props.TextStrokeTransparency = props.TextStrokeTransparency or 0.6
	return frame(props, parent)
end
for _, e in ipairs({{90, 1, 0.3, 0, 0}, {-90, 1, 0.3, 0, 0.7}, {0, 0.25, 1, 0, 0}, {180, 0.25, 1, 0.75, 0}}) do
	local f = frame({BackgroundColor3 = Color3.new(), Size = UDim2.fromScale(e[2], e[3]), Position = UDim2.fromScale(e[4], e[5])})
	local g = Instance.new("UIGradient")
	g.Rotation, g.Transparency = e[1], NumberSequence.new(0.05, 1)
	g.Parent = f
end
local dot = frame({AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), Size = UDim2.fromOffset(4, 4),
	BackgroundColor3 = Color3.new(1, 1, 1), BackgroundTransparency = 0.5})
Instance.new("UICorner", dot).CornerRadius = UDim.new(1, 0)
local barBack = frame({AnchorPoint = Vector2.new(0.5, 1), Position = UDim2.new(0.5, 0, 1, -92), Size = UDim2.fromOffset(200, 4),
	BackgroundColor3 = Color3.new(), BackgroundTransparency = 0.5, Visible = false})
local barFill = frame({Size = UDim2.fromScale(1, 1)}, barBack)
local chargeLabel = label({AnchorPoint = Vector2.new(0, 1), Position = UDim2.new(0.5, 176, 1, -30), Size = UDim2.fromOffset(90, 30),
	Font = Enum.Font.GothamBold, TextSize = 16, TextXAlignment = Enum.TextXAlignment.Left})
local toastLabel = label({Size = UDim2.new(1, 0, 0, 30), Position = UDim2.new(0, 0, 1, -130), Font = Enum.Font.GothamMedium,
	TextSize = 20, TextColor3 = Color3.fromRGB(235, 230, 220), TextTransparency = 1})
local fearFrame = frame({Size = UDim2.fromScale(1, 1), BackgroundColor3 = Color3.fromRGB(120, 0, 0), BackgroundTransparency = 1})
local toastId = 0
local function toast(text)
	toastId += 1
	local my = toastId
	toastLabel.Text, toastLabel.TextTransparency, toastLabel.TextStrokeTransparency = text, 0, 0.5
	task.delay(2.5, function()
		if toastId == my then TweenService:Create(toastLabel, TweenInfo.new(0.6), {TextTransparency = 1, TextStrokeTransparency = 1}):Play() end
	end)
end
local hint = label({Size = UDim2.new(1, 0, 0, 30), Position = UDim2.new(0, 0, 1, -165), Font = Enum.Font.Gotham, TextSize = 15,
	TextColor3 = Color3.fromRGB(200, 200, 200),
	Text = "E — подобрать  1–5 — в руку  ЛКМ/R — использовать  G — выбросить  F — фонарик  Shift — бег  C — присесть  Q/Z — выглянуть"})
task.delay(14, function() TweenService:Create(hint, TweenInfo.new(2), {TextTransparency = 1, TextStrokeTransparency = 1}):Play() end)

local paper = frame({AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.45), Size = UDim2.fromOffset(440, 320),
	BackgroundColor3 = Color3.fromRGB(225, 214, 182), Visible = false, ZIndex = 5})
local paperText = label({Position = UDim2.fromOffset(24, 20), Size = UDim2.new(1, -48, 1, -60), Font = Enum.Font.SourceSansItalic,
	TextSize = 24, TextWrapped = true, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top,
	TextColor3 = Color3.fromRGB(45, 30, 20), TextStrokeTransparency = 1, ZIndex = 6}, paper)
label({AnchorPoint = Vector2.new(0.5, 1), Position = UDim2.new(0.5, 0, 1, -8), Size = UDim2.new(1, 0, 0, 24), Font = Enum.Font.Gotham,
	TextSize = 14, TextColor3 = Color3.fromRGB(110, 90, 70), TextStrokeTransparency = 1, Text = "R / ЛКМ — закрыть", ZIndex = 6}, paper)
local reading = false
local function openNote(text) paperText.Text = text or "…"; paper.Visible = true; reading = true end
local function closeNote() paper.Visible = false; reading = false end

local function bigText(text, color, hold)
	local f = label({Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Text = text, Font = Enum.Font.Fantasy, TextSize = 46,
		TextColor3 = color, TextTransparency = 1, ZIndex = 10})
	f.BackgroundColor3 = Color3.new()
	TweenService:Create(f, TweenInfo.new(1.5), {BackgroundTransparency = 0.25, TextTransparency = 0}):Play()
	task.delay(hold, function()
		local t = TweenService:Create(f, TweenInfo.new(1.5), {BackgroundTransparency = 1, TextTransparency = 1})
		t:Play(); t.Completed:Wait(); f:Destroy()
	end)
end

---------------------------------------------------------------- ФОНАРИК
local torchOn, charge = true, 100
local clickT = 0
local function setTorch(on)
	if on and charge <= 0 then toast("Батарейка села. Найди новую"); on = false end
	torchOn = on
	spot.Enabled, glow.Enabled = on, on
	lens.Material = on and Enum.Material.Neon or Enum.Material.SmoothPlastic
	lens.Color = on and LIGHT or Color3.fromRGB(70, 70, 70)
	clickT = 0.25 -- большой палец жмёт кнопку
	playOnce(SOUNDS.click, 0.5)
end
task.spawn(function() -- мигает сам, чаще при слабой батарейке
	while true do
		task.wait(charge < 20 and math.random(2, 5) or math.random(10, 25))
		if torchOn then
			for _ = 1, math.random(3, 7) do
				spot.Enabled = false; task.wait(0.03 + math.random() * 0.08)
				spot.Enabled = torchOn; task.wait(0.03 + math.random() * 0.12)
			end
		end
	end
end)

---------------------------------------------------------------- ИНВЕНТАРЬ (модели из ItemModels)
local inventory, selected, heldEntries, heldModel = {}, nil, {}, nil
local slots, selectSlot = {}, nil
local reachT, inspectT = 0, 0
local HOLD = {
	battery = CFrame.Angles(rad(-70), 0, 0), key = CFrame.Angles(rad(10), rad(20), 0),
	note = CFrame.Angles(rad(70), 0, 0), amulet = CFrame.Angles(rad(60), 0, 0),
}
local function cloneItem(id, scale)
	local t = models:FindFirstChild(id)
	if not t then return nil end
	local m = t:Clone()
	if scale ~= 1 then m:ScaleTo(scale) end
	for _, p in ipairs(m:GetDescendants()) do
		if p:IsA("BasePart") then p.Anchored, p.CanCollide, p.CanQuery, p.CanTouch, p.CastShadow = true, false, false, false, false end
		if p:IsA("ProximityPrompt") or p:IsA("WeldConstraint") then p:Destroy() end
	end
	m:PivotTo(CFrame.new())
	return m
end
local function fillViewport(vp, data)
	vp:ClearAllChildren()
	local m = data and cloneItem(data.id, 1)
	if not m then return end
	m.Parent = vp
	local _, size = m:GetBoundingBox()
	local d = max(size.X, size.Y, size.Z) * 1.4
	local cam = Instance.new("Camera")
	cam.FieldOfView, cam.CFrame = 40, CFrame.lookAt(Vector3.new(d * 0.6, d * 0.8, d), Vector3.zero)
	cam.Parent = vp
	vp.CurrentCamera = cam
end
local function setHeld(data)
	for _, e in ipairs(heldEntries) do
		table.remove(vmParts, table.find(vmParts, e))
		e[1]:Destroy()
	end
	table.clear(heldEntries)
	if heldModel then heldModel:Destroy(); heldModel = nil end
	local m = data and cloneItem(data.id, 0.7)
	if not m then return end
	local base = lArm * CFrame.new(0.05, 0.25, -1.05) * (HOLD[data.id] or CFrame.new())
	for _, p in ipairs(m:GetDescendants()) do
		if p:IsA("BasePart") then
			local e = {p, base * p.CFrame, "L"}
			table.insert(heldEntries, e); table.insert(vmParts, e)
		end
	end
	m.Parent = vm
	heldModel = m
	inspectT = 1.2 -- достаёт и рассматривает
end
local bar = frame({AnchorPoint = Vector2.new(0.5, 1), Position = UDim2.new(0.5, 0, 1, -16),
	Size = UDim2.fromOffset(MAX_SLOTS * 68 - 8, 60), BackgroundTransparency = 1})
local layout = Instance.new("UIListLayout")
layout.FillDirection, layout.Padding, layout.SortOrder = Enum.FillDirection.Horizontal, UDim.new(0, 8), Enum.SortOrder.LayoutOrder
layout.Parent = bar
for i = 1, MAX_SLOTS do
	local b = frame({Class = "TextButton", LayoutOrder = i, Size = UDim2.fromOffset(60, 60), BackgroundColor3 = Color3.new(),
		BackgroundTransparency = 0.45, AutoButtonColor = false, Text = ""}, bar)
	Instance.new("UICorner", b).CornerRadius = UDim.new(0, 8)
	local stroke = Instance.new("UIStroke")
	stroke.ApplyStrokeMode, stroke.Color, stroke.Thickness, stroke.Transparency = Enum.ApplyStrokeMode.Border, Color3.new(1, 1, 1), 2, 0.85
	stroke.Parent = b
	local vp = frame({Class = "ViewportFrame", Position = UDim2.fromOffset(4, 4), Size = UDim2.new(1, -8, 1, -8), BackgroundTransparency = 1,
		Ambient = Color3.fromRGB(170, 170, 170), LightColor = Color3.new(1, 1, 1)}, b)
	label({Position = UDim2.fromOffset(5, 3), Size = UDim2.fromOffset(14, 14), Font = Enum.Font.GothamBold, TextSize = 12,
		TextColor3 = Color3.fromRGB(170, 170, 170), Text = tostring(i), ZIndex = 2}, b)
	b.Activated:Connect(function() selectSlot(i) end)
	slots[i] = {vp = vp, stroke = stroke}
end
local function refresh()
	if selected and not inventory[selected] then selected = nil end
	for i, s in ipairs(slots) do
		fillViewport(s.vp, inventory[i])
		s.stroke.Transparency = i == selected and 0 or 0.85
		s.stroke.Color = i == selected and Color3.fromRGB(255, 215, 140) or Color3.new(1, 1, 1)
	end
	setHeld(selected and inventory[selected])
end
function selectSlot(i)
	if reading then closeNote() end
	if selected == i or not inventory[i] then selected = nil else selected = i; toast(inventory[i].name) end
	refresh()
end
local function useHeld()
	if reading then closeNote(); return end
	local data = selected and inventory[selected]
	if not data then return end
	if data.id == "note" then openNote(data.text)
	elseif data.id == "battery" then
		if charge >= 99 then toast("Фонарик и так заряжен") else remote:FireServer("use", selected) end
	elseif data.id == "key" then toast("Старый ключ. Где-то должен быть замок…")
	elseif data.id == "amulet" then toast("Амулет тёплый и тихо пульсирует…") end
	inspectT = 0.8
end
local function dropHeld()
	if not (selected and inventory[selected]) then return end
	if reading then closeNote() end
	remote:FireServer("drop", selected)
	selected = nil
	refresh()
end

---------------------------------------------------------------- СМЕРТЬ: КАМЕРА
local cutscene = nil -- {monster, t0, ripT}
local function onCaught(monster)
	closeNote()
	cutscene = {monster = monster, t0 = os.clock()}
	workspace.CurrentCamera.CameraType = Enum.CameraType.Scriptable
	playOnce(SOUNDS.scream, 1)
end
local function onRip()
	if not cutscene then return end
	cutscene.ripT = os.clock()
	playOnce(SOUNDS.rip, 1)
	fearFrame.BackgroundColor3, fearFrame.BackgroundTransparency = Color3.fromRGB(200, 0, 0), 0
	TweenService:Create(fearFrame, TweenInfo.new(2.5), {BackgroundTransparency = 0.45}):Play()
	task.delay(1.6, function() bigText("ТЕБЯ ПОЙМАЛИ", Color3.fromRGB(200, 20, 20), 3.5) end)
end

remote.OnClientEvent:Connect(function(kind, value)
	if kind == "inv" then
		if #value < #inventory then selected = nil end
		inventory = value
		refresh()
	elseif kind == "msg" then toast(value)
	elseif kind == "battery" then charge = 100; setTorch(true); toast("Фонарик заряжен")
	elseif kind == "win" then bigText("ТЫ НАШЁЛ АМУЛЕТ\n\nДом наконец затих…", Color3.fromRGB(230, 200, 200), 7)
	elseif kind == "caught" then onCaught(value)
	elseif kind == "rip" then onRip() end
end)
remote:FireServer("sync")
PPS.PromptTriggered:Connect(function() reachT = 0.45 end) -- рука тянется за предметом

---------------------------------------------------------------- УПРАВЛЕНИЕ
local NUM = {[Enum.KeyCode.One] = 1, [Enum.KeyCode.Two] = 2, [Enum.KeyCode.Three] = 3, [Enum.KeyCode.Four] = 4, [Enum.KeyCode.Five] = 5}
local runHeld, crouch, leanL, leanR = false, false, false, false
UIS.InputBegan:Connect(function(input, gp)
	if gp or cutscene then return end
	local k = input.KeyCode
	if k == Enum.KeyCode.F then setTorch(not torchOn)
	elseif k == Enum.KeyCode.LeftShift then runHeld = true
	elseif k == Enum.KeyCode.C or k == Enum.KeyCode.LeftControl then crouch = not crouch
	elseif k == Enum.KeyCode.Q then leanL = true
	elseif k == Enum.KeyCode.Z then leanR = true
	elseif NUM[k] then selectSlot(NUM[k])
	elseif k == Enum.KeyCode.R or input.UserInputType == Enum.UserInputType.MouseButton1 then useHeld()
	elseif k == Enum.KeyCode.G then dropHeld() end
end)
UIS.InputEnded:Connect(function(input)
	local k = input.KeyCode
	if k == Enum.KeyCode.LeftShift then runHeld = false
	elseif k == Enum.KeyCode.Q then leanL = false
	elseif k == Enum.KeyCode.Z then leanR = false end
end)
if UIS.TouchEnabled then
	for _, b in ipairs({{"🔦", -170, -170, function() setTorch(not torchOn) end}, {"✋", -250, -170, useHeld},
		{"⬇", -170, -250, dropHeld}, {"🧎", -250, -250, function() crouch = not crouch end}}) do
		local btn = frame({Class = "TextButton", Size = UDim2.fromOffset(64, 64), Position = UDim2.new(1, b[2], 1, b[3]),
			BackgroundColor3 = Color3.new(), BackgroundTransparency = 0.5, Text = b[1], TextScaled = true, TextColor3 = Color3.new(1, 1, 1)})
		Instance.new("UICorner", btn).CornerRadius = UDim.new(1, 0)
		btn.Activated:Connect(b[4])
	end
end

---------------------------------------------------------------- ПЕРСОНАЖ
local stamina, exhausted, lastCF = STAMINA_MAX, false, nil
local landDip, wasAir = 0, false
local function onCharacter(char)
	stamina, exhausted, lastCF, crouch, cutscene = STAMINA_MAX, false, nil, false, nil
	fearFrame.BackgroundTransparency = 1
	local cam = workspace.CurrentCamera
	cam.CameraType, cam.FieldOfView = Enum.CameraType.Custom, BASE_FOV
	local hum = char:WaitForChild("Humanoid")
	hum.WalkSpeed = WALK
	if not player:HasAppearanceLoaded() then player.CharacterAppearanceLoaded:Wait() end
	local arm = char:FindFirstChild("RightHand") or char:FindFirstChild("Right Arm")
	if arm then for _, p in ipairs(skinParts) do p.Color = arm.Color end end
end
player.CharacterAdded:Connect(onCharacter)
if player.Character then task.spawn(onCharacter, player.Character) end

---------------------------------------------------------------- ВЗГЛЯД И СВЕТ → СЕРВЕР
local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude
local fear = 0
local function visible(from, part, monster)
	rayParams.FilterDescendantsInstances = {player.Character, vm}
	local hit = workspace:Raycast(from, part.Position - from, rayParams)
	return not hit or hit.Instance:IsDescendantOf(monster)
end
task.spawn(function()
	while true do
		task.wait(0.15)
		local folder = workspace:FindFirstChild("Monsters")
		local cam = workspace.CurrentCamera
		local looking, lit, nearest = {}, {}, 99
		if folder and not cutscene then
			local camCF, lpos = cam.CFrame, lightPart.Position
			for _, m in ipairs(folder:GetChildren()) do
				local head, torso = m:FindFirstChild("Head"), m:FindFirstChild("Torso")
				if head and torso then
					nearest = min(nearest, (torso.Position - camCF.Position).Magnitude)
					for _, p in ipairs({head, torso}) do
						local to = p.Position - camCF.Position
						if to.Unit:Dot(camCF.LookVector) > 0.55 and visible(camCF.Position, p, m) then
							if not table.find(looking, m.Name) then table.insert(looking, m.Name) end
						end
						local tl = p.Position - lpos
						local ang = math.deg(math.acos(clamp(tl.Unit:Dot(lightPart.CFrame.LookVector), -1, 1)))
						if torchOn and ang < TORCH_ANGLE and tl.Magnitude < TORCH_RANGE and visible(lpos, p, m) then
							if not table.find(lit, m.Name) then table.insert(lit, m.Name) end
						end
					end
				end
			end
		end
		fear = clamp(1 - (nearest - 6) / 30, 0, 1)
		remote:FireServer("sight", looking, lit)
	end
end)

---------------------------------------------------------------- КАЖДЫЙ КАДР
local bobT, bobAmt, runA, push, crouchA, leanA, heartT = 0, 0, 0, 0, 0, 0, 0
local sway = CFrame.new()
local RUN_POSE = CFrame.new(0.05, -0.15, 0.1) * CFrame.Angles(rad(-12), rad(10), rad(8))

local function deathCamera(camera, dt)
	local cs = cutscene
	local mon = cs.monster
	local head = mon and mon:FindFirstChild("Head")
	local char = player.Character
	local myHead = char and char:FindFirstChild("Head")
	vm.Parent = nil
	if not cs.ripT then -- смотрим монстру в лицо, всё трясётся
		local t = os.clock() - cs.t0
		local from = myHead and myHead.Position or camera.CFrame.Position
		local shake = Vector3.new(math.random() - 0.5, math.random() - 0.5, 0) * (t > 0.5 and 0.25 or 0.08)
		local goal = head and head.Position or from + camera.CFrame.LookVector
		camera.CFrame = camera.CFrame:Lerp(CFrame.lookAt(from + shake, goal), min(1, dt * 10))
		camera.FieldOfView += (55 - camera.FieldOfView) * min(1, dt * 3)
		fearFrame.BackgroundTransparency = 0.85 - min(t, 2) * 0.2
	else -- голова в лапе монстра, тело падает на пол — камера валится вместе с ним
		local t = os.clock() - cs.ripT
		local body = char and (char:FindFirstChild("UpperTorso") or char:FindFirstChild("Torso") or char:FindFirstChild("HumanoidRootPart"))
		if body then
			local fall = clamp((t - 0.6) / 1.2, 0, 1) ^ 2
			local p = body.Position + Vector3.new(0, 1.3 - fall * 1.6, 0)
			local tilt = CFrame.Angles(0, 0, rad(75) * fall)
			local look = (myHead and myHead.Position) or (head and head.Position) or p + Vector3.new(0, 0, -1)
			local target = CFrame.lookAt(p, look) * tilt
			camera.CFrame = camera.CFrame:Lerp(target, min(1, dt * 8))
		end
		camera.FieldOfView += (80 - camera.FieldOfView) * min(1, dt * 2)
	end
end

RunService:BindToRenderStep("HorrorFirstPerson", Enum.RenderPriority.Camera.Value + 1, function(dt)
	local camera = workspace.CurrentCamera
	if cutscene then deathCamera(camera, dt); return end
	local char = player.Character
	local hum = char and char:FindFirstChildOfClass("Humanoid")
	if not (hum and hum.Health > 0) then vm.Parent = nil; return end
	vm.Parent = camera
	local t = os.clock()

	if torchOn then
		charge = max(0, charge - BATTERY_DRAIN * dt)
		if charge <= 0 then setTorch(false); toast("Фонарик сел. Найди батарейку") end
	end
	spot.Brightness = TORCH_BRIGHT * clamp(0.35 + charge / 30, 0.35, 1)
	chargeLabel.Text = "🔦 " .. math.ceil(charge) .. "%"
	chargeLabel.TextColor3 = charge < 20 and Color3.fromRGB(230, 80, 70) or Color3.fromRGB(220, 220, 220)

	local moving = hum.MoveDirection.Magnitude > 0.1
	local running = runHeld and moving and not exhausted and not crouch
	if running then
		stamina = max(0, stamina - DRAIN * dt)
		if stamina <= 0 then exhausted = true end
	else
		stamina = min(STAMINA_MAX, stamina + REGEN * dt * (crouch and 1.4 or 1))
		if exhausted and stamina >= STAMINA_MAX * 0.35 then exhausted = false end
	end
	hum.WalkSpeed = crouch and CROUCH or running and RUN or WALK
	local a = min(1, dt * 7)
	camera.FieldOfView += ((running and RUN_FOV or BASE_FOV) - fear * 6 - camera.FieldOfView) * a
	barFill.Size = UDim2.fromScale(stamina / STAMINA_MAX, 1)
	barFill.BackgroundColor3 = exhausted and Color3.fromRGB(170, 40, 40) or Color3.fromRGB(220, 220, 220)
	barBack.Visible = stamina < STAMINA_MAX

	-- приземление: камера проседает
	local inAir = hum.FloorMaterial == Enum.Material.Air
	if wasAir and not inAir then landDip = 0.35 end
	wasAir = inAir
	landDip = max(0, landDip - dt * 1.2)

	-- страх: сердцебиение, красные края, дрожь
	heartT += dt * (1.2 + fear * 2.2)
	local beat = max(0, sin(heartT * math.pi * 2)) ^ 8 * fear
	fearFrame.BackgroundTransparency = 1 - beat * 0.25
	if heartSnd then heartSnd.Volume, heartSnd.PlaybackSpeed = fear * 0.9, 1 + fear * 0.6 end

	local onGround = not inAir
	bobAmt += (((moving and onGround) and (running and 1.8 or crouch and 0.6 or 1) or 0) - bobAmt) * a
	runA += ((running and 1 or 0) - runA) * a
	crouchA += ((crouch and 1 or 0) - crouchA) * min(1, dt * 8)
	leanA += (((leanL and 1 or 0) - (leanR and 1 or 0)) - leanA) * min(1, dt * 8)
	bobT += dt * (running and 13 or crouch and 6 or 8.5)
	local bx = sin(bobT) * 0.05 * bobAmt
	local by = abs(cos(bobT)) * 0.06 * bobAmt
	local breathe = sin(t * (1.7 + fear * 3 + (exhausted and 3 or 0))) * (0.015 + (exhausted and 0.02 or 0))
	local tremble = fear * 0.006

	local camCF = camera.CFrame
	if lastCF then
		local rx, ry = lastCF:ToObjectSpace(camCF):ToOrientation()
		local k = 1 / max(dt * 60, 0.001)
		sway = sway:Lerp(CFrame.Angles(clamp(-rx * k * 0.8, -0.15, 0.15), clamp(-ry * k * 0.8, -0.15, 0.15), 0), min(1, dt * 10))
	end
	-- приседание, наклон и качка камеры
	hum.CameraOffset = Vector3.new(-leanA * 1.3, -crouchA * 1.6 - landDip * 2, 0)
	camera.CFrame = camCF * CFrame.new(bx * 0.3, by * 0.4, 0)
		* CFrame.Angles((math.random() - 0.5) * tremble, (math.random() - 0.5) * tremble, bx * 0.12 + leanA * rad(14))
	lastCF = camCF

	rayParams.FilterDescendantsInstances = {char, vm}
	local hit = workspace:Raycast(camCF.Position, camCF.LookVector * 2.2, rayParams)
	push += ((hit and (2.2 - hit.Distance) or 0) - push) * min(1, dt * 15)

	-- жесты рук
	reachT, inspectT, clickT = max(0, reachT - dt), max(0, inspectT - dt), max(0, clickT - dt)
	local reach = sin(clamp(reachT / 0.45, 0, 1) * math.pi)
	local inspect = sin(clamp(inspectT / 1.2, 0, 1) * math.pi)
	local L = CFrame.new(0.25 * reach + 0.1 * inspect, 0.15 * reach + 0.12 * inspect, -0.6 * reach)
		* CFrame.Angles(rad(15) * inspect, rad(25) * inspect + rad(20) * reach, sin(t * 3) * 0.1 * inspect)
	local R = CFrame.new(0, -0.03 * sin(clamp(clickT / 0.25, 0, 1) * math.pi), 0)
	button.Color = torchOn and Color3.fromRGB(200, 40, 40) or Color3.fromRGB(90, 20, 20)

	local root = camera.CFrame * sway * CFrame.new():Lerp(RUN_POSE, runA)
		* CFrame.new(bx, by + breathe - push * 0.3 - crouchA * 0.05, push * 0.8)
		* CFrame.Angles(0, 0, -leanA * rad(6))
	for _, item in ipairs(vmParts) do
		local g = item[3] == "L" and L or item[3] == "R" and R or CFrame.new()
		item[1].CFrame = root * g * item[2]
	end
end)
]]></ProtectedString></Properties></Item></Item></Item>
</roblox>