function TargetBoxPool.Cleanup(D)D.Adornee=nil D.Visible=nil end function TargetingModule.HighlightTarget(D)TargetBoxPool:ReleaseAll()if not D then return end local d=Support.FlipTable({D})if not IsVisible(D)then d=Support.FlipTable(GetVisibleChildren(D))end for D in pairs(d)do local d=TargetBoxPool:Get()d.Adornee=D d.Visible=true end end local function D(D)while D and D~=TargetingModule.Scope do if Selection.IsSelected(D)then return true else D=D.Parent end end end function TargetingModule.SelectTarget(d)local c=TargetingModule.Scope local J,K=TargetingModule:UpdateTarget(c,true)if not d and SelectionCancelled then SelectionCancelled=false return end if not Selection.Multiselecting and(Selection.IsSelected(K)or D(K))then Selection.SetFocus(K)return end if not(GetCore()).IsSelectable({J})then Selection.Clear(true)return end if Selection.Multiselecting and Selection.IsSelected(K)then Selection.Remove({K},true)return end if Selection.Multiselecting then Selection.Add({K},true)Selection.SetFocus(K)else Selection.Replace({K},true)Selection.SetFocus(K)end end function TargetingModule.SelectSiblings(D,d)local c=D or Selection.Focus if not c or c.Parent==TargetingModule.Scope then return end local J=Support.GetDescendantsWhichAreA(c.Parent,"\066\097\115\101\080\097\114\116")if not(GetCore()).IsSelectable(J)then return end if d then Selection.Replace(J,true)else Selection.Add(J,true)end end function TargetingModule.StartRectangleSelecting()if SelectionCancelled then return end RectangleSelectStart=Vector2.new(Mouse.X,Mouse.Y);(GetCore()).Connections.WatchRectangleSelection=Mouse.Move:Connect(function()if RectangleSelecting then TargetingModule.UpdateSelectionRectangle()elseif RectangleSelectStart and(Vector2.new(Mouse.X,Mouse.Y)-RectangleSelectStart).magnitude>=10 then RectangleSelecting=true SelectionCancelled=true end end)end function TargetingModule.UpdateSelectionRectangle()if not RectangleSelecting then return end local D=GetCore()if not SelectionRectangle then SelectionRectangle=(Make("\070\114\097\109\101"))({Name="\083\101\108\101\099\116\105\111\110\082\101\099\116\097\110\103\108\101";Parent=D.UI,BackgroundColor3=Color3.fromRGB(100,100,100);BorderColor3=Color3.new(0,0,0);BackgroundTransparency=.5;BorderSizePixel=1})end local d=Vector2.new(math.min(RectangleSelectStart.X,Mouse.X),math.min(RectangleSelectStart.Y,Mouse.Y))local c=Vector2.new(math.max(RectangleSelectStart.X,Mouse.X),math.max(RectangleSelectStart.Y,Mouse.Y))SelectionRectangle.Parent=D.UI SelectionRectangle.Position=UDim2.new(0,d.X,0,d.Y)SelectionRectangle.Size=UDim2.new(0,c.X-d.X,0,c.Y-d.Y)end function TargetingModule.CancelRectangleSelecting()RectangleSelectStart=nil RectangleSelecting=false local D=(GetCore()).Connections if D.WatchRectangleSelection then D.WatchRectangleSelection:Disconnect()D.WatchRectangleSelection=nil end if SelectionRectangle then SelectionRectangle.Parent=nil end end function TargetingModule.CancelSelecting()SelectionCancelled=true TargetingModule.CancelRectangleSelecting()end function TargetingModule.FinishRectangleSelecting()local D=GetCore()local d=RectangleSelecting local c=RectangleSelectStart TargetingModule.CancelRectangleSelecting()if not d then return end if not TargetingModule.Scope then return end local J=Vector2.new(math.min(c.X,Mouse.X),math.min(c.Y,Mouse.Y))local K=Vector2.new(math.max(c.X,Mouse.X),math.max(c.Y,Mouse.Y))local r={}local b=Support.GetDescendantsWhichAreA(TargetingModule.Scope,"\066\097\115\101\080\097\114\116")for d,c in pairs(b)do local b,f=Workspace.CurrentCamera:WorldToScreenPoint(c.Position)if f then local d=b.X>=J.X local f=b.X<=K.X local u=b.Y>=J.Y local k=b.Y<=K.Y if(d and(f and(u and k)))and D.IsSelectable({c})then local D=TargetingModule:FindTargetInScope(c,TargetingModule.Scope)r[D]=true end end end if Selection.Multiselecting then Selection.Add(Support.Keys(r),true)else Selection.Replace(Support.Keys(r),true)end end function TargetingModule.PrismSelect()if#Selection.Items==0 then return end local D=GetCore()local d=(require(D.Tool.Core.BoundingBox)).CalculateExtents(Selection.Items,nil,true)local c=Region3.new(d.Min,d.Max)local J=Workspace:FindPartsInRegion3WithIgnoreList(c,Selection.Items,math.huge)local K={}for D,d in pairs(J)do K[d]={Anchored=d.Anchored;CanCollide=d.CanCollide}d.Anchored=true d.CanCollide=true end local r={}for D,d in pairs(Selection.Items)do local c=d:GetTouchingParts()for D,d in pairs(c)do if not Selection.IsSelected(d)then r[d]=true end end end for D,d in pairs(K)do D.CanCollide=d.CanCollide D.Anchored=d.Anchored end D.DeleteSelection()Selection.Replace(Support.Keys(r),true)end function TargetingModule.EnableScopeSelection(D)local d=false local c=nil local function J(J,K,r)if K.Name=="\066\101\103\105\110"then local J=UserInputService:IsKeyDown("\076\101\102\116\065\108\116")or UserInputService:IsKeyDown("\082\105\103\104\116\065\108\116")local K=UserInputService:IsKeyDown("\076\101\102\116\083\104\105\102\116")or UserInputService:IsKeyDown("\082\105\103\104\116\083\104\105\102\116")if not d and r.KeyCode.Name:match("\065\108\116")then d=D.Scope c=D.Scope local J,K=D:UpdateTarget(D.Scope,true)if J~=K then D:SetScope(K or D.Scope)D:UpdateTarget(D.Scope,true)D.IsScopeLocked=false D.ScopeLockChanged:Fire(false)d=D.Scope end elseif d and(J and(K and r.KeyCode.Name=="\090"))then local J=D.Scope.Parent or c if(GetCore()).Security.IsLocationAllowed(J,(GetCore()).Player)then D:SetScope(J)D:UpdateTarget(D.Scope,true)D.IsScopeLocked=false D.ScopeLockChanged:Fire(false)d=D.Scope end return Enum.ContextActionResult.Sink elseif d and(J and r.KeyCode.Name=="\090")then local c,J=D:UpdateTarget(D.Scope,true)if c~=J then D:SetScope(J or D.Scope)D:UpdateTarget(D.Scope,true)D.IsScopeLocked=false D.ScopeLockChanged:Fire(false)d=D.Scope end return Enum.ContextActionResult.Sink elseif d and(J and r.KeyCode.Name=="\070")then d=true D.IsScopeLocked=true D.ScopeLockChanged:Fire(true)return Enum.ContextActionResult.Sink end elseif K.Name=="\069\110\100"then if d and r.KeyCode.Name:match("\065\108\116")then if D.Scope==d then D:SetScope(c)D.IsScopeLocked=true D.ScopeLockChanged:Fire(true)end D:UpdateTarget(D.Scope,true)d=nil c=nil end end if d and r.UserInputType.Name=="\070\111\099\117\115"then if D.Scope==d then D:SetScope(c)D.IsScopeLocked=true D.ScopeLockChanged:Fire(true)end D:UpdateTarget(D.Scope,true)d=nil c=nil end return Enum.ContextActionResult.Pass end ContextActionService:BindAction("\066\084\058 \083\099\111\112\101",J,false,Enum.KeyCode.LeftAlt,Enum.KeyCode.RightAlt,Enum.KeyCode.Z,Enum.KeyCode.F,Enum.UserInputType.Focus);(GetCore()).Disabling:Connect(function()ContextActionService:UnbindAction("\066\084\058 \083\099\111\112\101")end)end function TargetingModule.EnableScopeAutoReset(D)local d,c;(GetCore()).UIMaid.ScopeReset=D.ScopeChanged:Connect(function(J)d=d and d:Disconnect()if not J then return end c={}local K=J.Parent while K:IsDescendantOf(Workspace)do table.insert(c,K)K=K.Parent end d=J.AncestryChanged:Connect(function(d,K)if K==nil then local d if c then for D,c in ipairs(c)do if c:IsDescendantOf(Workspace)then d=c break end end end if d then D:SetScope(d)else D:SetScope(Workspace,true)end else c={}local D=J.Parent while D:IsDescendantOf(Workspace)do table.insert(c,D)D=D.Parent end end end)end)end

function TargetBoxPool.Cleanup(TargetBox)
	TargetBox.Adornee = nil
	TargetBox.Visible = nil
end

function TargetingModule.HighlightTarget(Target)

	-- Clear previous target boxes
	TargetBoxPool:ReleaseAll()

	-- Make sure target exists
	if not Target then
		return
	end

	-- Get targetable items
	local Items = Support.FlipTable { Target }
	if not IsVisible(Target) then
		Items = Support.FlipTable(GetVisibleChildren(Target))
	end

	-- Focus target boxes on target
	for Target in pairs(Items) do
		local TargetBox = TargetBoxPool:Get()
		TargetBox.Adornee = Target
		TargetBox.Visible = true
	end

end;

local function IsAncestorSelected(Item)
	while Item and (Item ~= TargetingModule.Scope) do
		if Selection.IsSelected(Item) then
			return true
		else
			Item = Item.Parent
		end
	end
end

function TargetingModule.SelectTarget(Force)
	local Scope = TargetingModule.Scope

	-- Update target
	local Target, ScopeTarget = TargetingModule:UpdateTarget(Scope, true)

	-- Ensure target selection isn't cancelled
	if not Force and SelectionCancelled then
		SelectionCancelled = false;
		return;
	end;

	-- Focus on clicked, selected item
	if not Selection.Multiselecting and (Selection.IsSelected(ScopeTarget) or IsAncestorSelected(ScopeTarget)) then
		Selection.SetFocus(ScopeTarget)
		return;
	end;

	-- Clear selection if invalid target selected
	if not GetCore().IsSelectable({ Target }) then
		Selection.Clear(true);
		return;
	end;

	-- Unselect clicked, selected item if multiselection is enabled
	if Selection.Multiselecting and Selection.IsSelected(ScopeTarget) then
		Selection.Remove({ ScopeTarget }, true)
		return
	end

	-- Add to selection if multiselecting
	if Selection.Multiselecting then
		Selection.Add({ ScopeTarget }, true)
		Selection.SetFocus(ScopeTarget)

	-- Replace selection if not multiselecting
	else
		Selection.Replace({ ScopeTarget }, true)
		Selection.SetFocus(ScopeTarget)
	end

end;

function TargetingModule.SelectSiblings(Part, ReplaceSelection)
	-- Selects all parts under the same parent as `Part`

	-- If a part is not specified, assume the currently focused part
	local Part = Part or Selection.Focus;

	-- Ensure the part exists and its parent is not Workspace
	if not Part or Part.Parent == TargetingModule.Scope then
		return;
	end;

	-- Get the focused item's siblings
	local Siblings = Support.GetDescendantsWhichAreA(Part.Parent, 'BasePart')

	-- Ensure items are selectable
	if not GetCore().IsSelectable(Siblings) then
		return
	end

	-- Add to or replace selection
	if ReplaceSelection then
		Selection.Replace(Siblings, true);
	else
		Selection.Add(Siblings, true);
	end;

end;

function TargetingModule.StartRectangleSelecting()

	-- Ensure selection isn't cancelled
	if SelectionCancelled then
		return;
	end;

	-- Mark where rectangle selection started
	RectangleSelectStart = Vector2.new(Mouse.X, Mouse.Y);

	-- Track mouse while rectangle selecting
	GetCore().Connections.WatchRectangleSelection = Mouse.Move:Connect(function ()

		-- If rectangle selecting, update rectangle
		if RectangleSelecting then
			TargetingModule.UpdateSelectionRectangle();

		-- Watch for potential rectangle selections
		elseif RectangleSelectStart and (Vector2.new(Mouse.X, Mouse.Y) - RectangleSelectStart).magnitude >= 10 then
			RectangleSelecting = true;
			SelectionCancelled = true;
		end;

	end);

end;

function TargetingModule.UpdateSelectionRectangle()

	-- Ensure rectangle selection is ongoing
	if not RectangleSelecting then
		return;
	end;

	-- Get core API
	local Core = GetCore();

	-- Create selection rectangle
	if not SelectionRectangle then
		SelectionRectangle = Make 'Frame' {
			Name = 'SelectionRectangle',
			Parent = Core.UI,
			BackgroundColor3 = Color3.fromRGB(100, 100, 100),
			BorderColor3 = Color3.new(0, 0, 0),
			BackgroundTransparency = 0.5,
			BorderSizePixel = 1
		};
	end;

	local StartPoint = Vector2.new(
		math.min(RectangleSelectStart.X, Mouse.X),
		math.min(RectangleSelectStart.Y, Mouse.Y)
	);
	local EndPoint = Vector2.new(
		math.max(RectangleSelectStart.X, Mouse.X),
		math.max(RectangleSelectStart.Y, Mouse.Y)
	);

	-- Update size and position
	SelectionRectangle.Parent = Core.UI;
	SelectionRectangle.Position = UDim2.new(0, StartPoint.X, 0, StartPoint.Y);
	SelectionRectangle.Size = UDim2.new(0, EndPoint.X - StartPoint.X, 0, EndPoint.Y - StartPoint.Y);

end;

function TargetingModule.CancelRectangleSelecting()

	-- Prevent potential rectangle selections
	RectangleSelectStart = nil;

	-- Clear ongoing rectangle selection
	RectangleSelecting = false;

	-- Clear rectangle selection watcher
	local Connections = GetCore().Connections;
	if Connections.WatchRectangleSelection then
		Connections.WatchRectangleSelection:Disconnect();
		Connections.WatchRectangleSelection = nil;
	end;

	-- Clear rectangle UI
	if SelectionRectangle then
		SelectionRectangle.Parent = nil;
	end;

end;

function TargetingModule.CancelSelecting()
	SelectionCancelled = true;
	TargetingModule.CancelRectangleSelecting();
end;

function TargetingModule.FinishRectangleSelecting()
	local Core = GetCore()

	local RectangleSelecting = RectangleSelecting;
	local RectangleSelectStart = RectangleSelectStart;

	-- Clear rectangle selection
	TargetingModule.CancelRectangleSelecting();

	-- Ensure rectangle selection is ongoing
	if not RectangleSelecting then
		return;
	end;

	-- Ensure a targeting scope is set
	if not TargetingModule.Scope then
		return
	end

	-- Get rectangle dimensions
	local StartPoint = Vector2.new(
		math.min(RectangleSelectStart.X, Mouse.X),
		math.min(RectangleSelectStart.Y, Mouse.Y)
	);
	local EndPoint = Vector2.new(
		math.max(RectangleSelectStart.X, Mouse.X),
		math.max(RectangleSelectStart.Y, Mouse.Y)
	);

	local SelectableItems = {};

	-- Find items that lie within the rectangle
	local ScopeParts = Support.GetDescendantsWhichAreA(TargetingModule.Scope, 'BasePart')
	for _, Part in pairs(ScopeParts) do
		local ScreenPoint, OnScreen = Workspace.CurrentCamera:WorldToScreenPoint(Part.Position)
		if OnScreen then
			local LeftCheck = ScreenPoint.X >= StartPoint.X
			local RightCheck = ScreenPoint.X <= EndPoint.X
			local TopCheck = ScreenPoint.Y >= StartPoint.Y
			local BottomCheck = ScreenPoint.Y <= EndPoint.Y
			if (LeftCheck and RightCheck and TopCheck and BottomCheck) and Core.IsSelectable({ Part }) then
				local ScopeTarget = TargetingModule:FindTargetInScope(Part, TargetingModule.Scope)
				SelectableItems[ScopeTarget] = true
			end
		end
	end

	-- Add to selection if multiselecting
	if Selection.Multiselecting then
		Selection.Add(Support.Keys(SelectableItems), true)

	-- Replace selection if not multiselecting
	else
		Selection.Replace(Support.Keys(SelectableItems), true)
	end;

end;

function TargetingModule.PrismSelect()
	-- Selects parts in the currently selected parts

	-- Ensure parts are selected
	if #Selection.Items == 0 then
		return;
	end;

	-- Get core API
	local Core = GetCore();

	-- Get region for selection items and find potential parts
	local Extents = require(Core.Tool.Core.BoundingBox).CalculateExtents(Selection.Items, nil, true);
	local Region = Region3.new(Extents.Min, Extents.Max);
	local PotentialParts = Workspace:FindPartsInRegion3WithIgnoreList(Region, Selection.Items, math.huge);

	-- Enable collision on all potential parts
	local OriginalState = {};
	for _, PotentialPart in pairs(PotentialParts) do
		OriginalState[PotentialPart] = { Anchored = PotentialPart.Anchored, CanCollide = PotentialPart.CanCollide };
		PotentialPart.Anchored = true;
		PotentialPart.CanCollide = true;
	end;

	local Parts = {};

	-- Find all parts intersecting with selection
	for _, Part in pairs(Selection.Items) do
		local TouchingParts = Part:GetTouchingParts();
		for _, TouchingPart in pairs(TouchingParts) do
			if not Selection.IsSelected(TouchingPart) then
				Parts[TouchingPart] = true;
			end;
		end;
	end;

	-- Restore all potential parts' original states
	for PotentialPart, State in pairs(OriginalState) do
		PotentialPart.CanCollide = State.CanCollide;
		PotentialPart.Anchored = State.Anchored;
	end;

	-- Delete the selection parts
	Core.DeleteSelection();

	-- Select all found parts
	Selection.Replace(Support.Keys(Parts), true);

end;

function TargetingModule:EnableScopeSelection()
	-- Enables the scope selection interface

	-- Set up state
	local Scoping = false
	local InitialScope = nil

	local function HandleScopeInput(Action, State, Input)
		if State.Name == 'Begin' then
			local IsAltPressed = UserInputService:IsKeyDown 'LeftAlt' or
				UserInputService:IsKeyDown 'RightAlt'
			local IsShiftPressed = UserInputService:IsKeyDown 'LeftShift' or
				UserInputService:IsKeyDown 'RightShift'

			-- If Alt is pressed, begin scoping
			if (not Scoping) and (Input.KeyCode.Name:match 'Alt') then
				Scoping = self.Scope
				InitialScope = self.Scope

				-- Set new scope to current scope target
				local Target, ScopeTarget = self:UpdateTarget(self.Scope, true)
				if Target ~= ScopeTarget then
					self:SetScope(ScopeTarget or self.Scope)
					self:UpdateTarget(self.Scope, true)
					self.IsScopeLocked = false
					self.ScopeLockChanged:Fire(false)
					Scoping = self.Scope
				end

			-- If Alt-Shift-Z is pressed, exit current scope
			elseif Scoping and IsAltPressed and IsShiftPressed and (Input.KeyCode.Name == 'Z') then
				local NewScope = self.Scope.Parent or InitialScope
				if GetCore().Security.IsLocationAllowed(NewScope, GetCore().Player) then
					self:SetScope(NewScope)
					self:UpdateTarget(self.Scope, true)
					self.IsScopeLocked = false
					self.ScopeLockChanged:Fire(false)
					Scoping = self.Scope
				end
				return Enum.ContextActionResult.Sink

			-- If Alt-Z is pressed, enter scope of current target
			elseif Scoping and IsAltPressed and (Input.KeyCode.Name == 'Z') then
				local Target, ScopeTarget = self:UpdateTarget(self.Scope, true)
				if Target ~= ScopeTarget then
					self:SetScope(ScopeTarget or self.Scope)
					self:UpdateTarget(self.Scope, true)
					self.IsScopeLocked = false
					self.ScopeLockChanged:Fire(false)
					Scoping = self.Scope
				end
				return Enum.ContextActionResult.Sink

			-- If Alt-F is pressed, stay in current scope
			elseif Scoping and IsAltPressed and (Input.KeyCode.Name == 'F') then
				Scoping = true
				self.IsScopeLocked = true
				self.ScopeLockChanged:Fire(true)
				return Enum.ContextActionResult.Sink
			end

		-- Disable scoping on Alt release
		elseif State.Name == 'End' then
			if Scoping and (Input.KeyCode.Name:match 'Alt') then
				if self.Scope == Scoping then
					self:SetScope(InitialScope)
					self.IsScopeLocked = true
					self.ScopeLockChanged:Fire(true)
				end
				self:UpdateTarget(self.Scope, true)
				Scoping = nil
				InitialScope = nil
			end
		end

		-- If window focus changes, reset and disable scoping
		if Scoping and Input.UserInputType.Name == 'Focus' then
			if self.Scope == Scoping then
				self:SetScope(InitialScope)
				self.IsScopeLocked = true
				self.ScopeLockChanged:Fire(true)
			end
			self:UpdateTarget(self.Scope, true)
			Scoping = nil
			InitialScope = nil
		end

		-- Pass all non-sunken input to next handler
		return Enum.ContextActionResult.Pass
	end

	-- Enable scoping interface
	ContextActionService:BindAction('BT: Scope', HandleScopeInput, false,
		Enum.KeyCode.LeftAlt,
		Enum.KeyCode.RightAlt,
		Enum.KeyCode.Z,
		Enum.KeyCode.F,
		Enum.UserInputType.Focus
	)

	-- Disable scoping interface when tool disables
	GetCore().Disabling:Connect(function ()
		ContextActionService:UnbindAction('BT: Scope')
	end)

end

function TargetingModule:EnableScopeAutoReset()
	-- Enables automatic scope resetting (when scope becomes invalid)

	local LastScopeListener, LastScopeAncestry

	-- Listen to changes in scope
	GetCore().UIMaid.ScopeReset = self.ScopeChanged:Connect(function (Scope)

		-- Clear last scope listener
		LastScopeListener = LastScopeListener and LastScopeListener:Disconnect()

		-- Only listen to new scope if defined
		if not Scope then
			return
		end

		-- Capture new scope's ancestry
		LastScopeAncestry = {}
		local Ancestor = Scope.Parent
		while Ancestor:IsDescendantOf(Workspace) do
			table.insert(LastScopeAncestry, Ancestor)
			Ancestor = Ancestor.Parent
		end

		-- Reset scope when scope is gone
		LastScopeListener = Scope.AncestryChanged:Connect(function (_, Parent)
			if Parent == nil then

				-- Get next parent in ancestry
				local NextScopeInAncestry
				if LastScopeAncestry then
					for _, Parent in ipairs(LastScopeAncestry) do
						if Parent:IsDescendantOf(Workspace) then
							NextScopeInAncestry = Parent
							break
						end
					end
				end

				-- Set next scope
				if NextScopeInAncestry then
					self:SetScope(NextScopeInAncestry)
				else
					self:SetScope(Workspace, true)
				end

			-- Capture scope ancestry when it changes
			else
				LastScopeAncestry = {}
				local Ancestor = Scope.Parent
				while Ancestor:IsDescendantOf(Workspace) do
					table.insert(LastScopeAncestry, Ancestor)
					Ancestor = Ancestor.Parent
				end
			end
		end)

	end)
end
