Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --by imalex4
- --The settings are pretty good as they are.
- --SOME SETTINGS
- --================================================================================================================================
- --NOTE: if you only want certain people to be able to use the body scanner screen, then go to line 1015
- local clickdistance = 12 --the max distance (from the humanoid's head) at which a character can use the body scanner's screen
- local increment = .05 --this is how often a ray is cast; if this were .5, a ray would be cast at the item every .5 studs; the lower the number, the more detail BUT ALSO possibly more lag
- --NOTE: for really small items (scan area of less than 9 square studs) that need to be scanned in more detail, this number will be divided in two
- local scanlocation = CFrame.new(0, -200, 0) --scanned items must be brought into Workspace to be scanned; this is where they will be brought in
- local ignoretransparentitems = true --transparent items will not be scanned
- --=========================================================================================================================================
- local numofscanimages
- local numofpeopleinscanner
- local hinge = script.Parent.Parent.Parent.rotatingassembly.poweredpart.HingeConstraint
- local transparentname = 'transparent' .. math.random(1, 9999)
- local raycaststartdistance = .1 --this is how far back from the object's bounding box we will start the raycast; example: if we want to raycast to the top of the object and the top of object's bounding box is at Y = 10, then we will start raycast at Y = 10 + raycaststartdistance
- local currentitem --this is the part/model that is currently being scanned
- local dotsize = 2 --the scan image will be made of many dots; this is the size of each dot
- local makedotinfo = {}
- local generalinfoarray = {} --layout: generalinfoarray[dotholder] has { ['message'], ['itemowner'], ['currentitemholder'], ['scannedthing']}
- local peopleinscanner = {} --machine will only scan if there is only one person in this table; each item will be a character model
- local debounces = {} --true means you cannot do the function
- local gui = script.Parent.SurfaceGui
- local interface = gui.interface
- local scanimages
- local scanimagesindex = 0 --set to 1 when images are loaded
- local timeofscanstart = 0
- debounces['previousimage'] = true
- debounces['nextimage'] = true
- script.Parent.dot.Size = UDim2.new(0, dotsize, 0, dotsize)
- if not game.ServerStorage:FindFirstChild('meshpartsforscanner') or game.ServerStorage.meshpartsforscanner.ClassName ~= 'Folder' then
- print("NOTE: you need to follow the instructions inside of bodyscanner.readme")
- end
- --==========this is not used, as I found out about GetBoundingBox ================NOTE: not tested
- function getcenterofmodel(model) --it is expected that 'model' is a model and it has a primary part and all meshes have been removed with 'changemeshesformodel'; center is returned as a vector3 in world coordinates
- --NOTE: not tested and note used
- local lowestx, greatestx, lowesty, greatesty, lowestz, greatestz
- for _, v in pairs(model:GetDescendants()) do
- if v and v:IsA('BasePart') then
- local position = v.Position
- local size = v.Size
- local edges = {} --edges will be 1 through 8 and they go in this order: upper left, upper right, lower left, lower right of the brick's left face (from a perspective of looking at the left face of a standard brick) and then upper left, upper right, lower left, lower right from the perspective of looking at the right face
- local middleofface = v.CFrame:ToWorldSpace(CFrame.new(v.Size.X/-2, 0, 0)) --this represents the cframe which is the center of the left or right face of the brick
- edges[1] = (middleofface * CFrame.new(0, (v.Size.Y / 2), 0) * CFrame.new(0, 0, v.Size.Z / -2)).Position
- edges[2] = (middleofface * CFrame.new(0, (v.Size.Y / 2), 0) * CFrame.new(0, 0, v.Size.Z / 2)).Position
- edges[3] = (middleofface * CFrame.new(0, (v.Size.Y / -2), 0) * CFrame.new(0, 0, v.Size.Z / -2)).Position
- edges[4] = (middleofface * CFrame.new(0, (v.Size.Y / -2), 0) * CFrame.new(0, 0, v.Size.Z / 2)).Position
- middleofface = v.CFrame:ToWorldSpace(CFrame.new(v.Size.X/2, 0, 0))
- edges[5] = (middleofface * CFrame.new(0, (v.Size.Y / 2), 0) * CFrame.new(0, 0, v.Size.Z / 2)).Position
- edges[6] = (middleofface * CFrame.new(0, (v.Size.Y / 2), 0) * CFrame.new(0, 0, v.Size.Z / -2)).Position
- edges[7] = (middleofface * CFrame.new(0, (v.Size.Y / -2), 0) * CFrame.new(0, 0, v.Size.Z / 2)).Position
- edges[8] = (middleofface * CFrame.new(0, (v.Size.Y / -2), 0) * CFrame.new(0, 0, v.Size.Z / -2)).Position
- for i = 1, 8 do
- if not lowestx or edges[i].X < lowestx then
- lowestx = edges[i].X
- end
- if not greatestx or edges[i].X > greatestx then
- greatestx = edges[i].X
- end
- if not lowesty or edges[i].Y < lowesty then
- lowesty = edges[i].Y
- end
- if not greatesty or edges[i].Y > greatesty then
- greatesty = edges[i].Y
- end
- if not lowestz or edges[i].Z < lowestz then
- lowestz = edges[i].Z
- end
- if not greatestz or edges[i].Z > greatestz then
- greatestz = edges[i].Z
- end
- end
- end
- end--for _, v in pairs
- local center = Vector3.new( (lowestx + greatestx) / 2, (lowesty + greatesty) / 2, (lowestz + greatestz) / 2 )
- end
- function giveprimarypart(currentitem, model) --in case we go into recursion, currentitem keeps track of where we are (whose children are we looking through)
- if model:IsA("Model") and model.PrimaryPart == nil then
- for _, v in pairs(currentitem:GetChildren()) do
- if v and v:IsA("BasePart") then
- model.PrimaryPart = v
- break
- end
- end --for _, v
- end --if model:IsA
- if not model.PrimaryPart then
- for _, v in pairs(currentitem:GetChildren()) do
- giveprimarypart(v, model)
- end
- end
- end
- function changemeshesformodel(thing, newmodel) --thing is the object/model whose children we are checking; newmodel is the new model that we are cloning everything into;
- if thing:IsA('BasePart') and (thing.Name ~= transparentname or ignoretransparentitems == false) then
- local themesh
- --I am doing this for loop because the part could have multiple meshes; if a part has multiple meshes, it uses the most recent mesh, and the most recent mesh is the last one to be iterated through in "in pairs"
- for _, v in pairs(thing:GetChildren()) do
- if v and v:IsA("DataModelMesh") then
- themesh = v
- end --if v and v:IsA('DataModelMesh')
- end --for _, v in pairs
- if not themesh then
- local copy = thing:Clone()
- copy:ClearAllChildren()
- copy.Anchored = true
- copy.CanCollide = false
- copy.Transparency = 1
- copy.Name = thing.Name
- copy.Parent = newmodel
- elseif themesh.ClassName == "BlockMesh" then
- local newpart = Instance.new("Part")
- newpart.Size = thing.Size * themesh.Scale
- newpart.CFrame = thing.CFrame * CFrame.new(themesh.Offset)
- newpart.Name = thing.Name
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = newmodel
- elseif themesh.ClassName == "CylinderMesh" then
- local newpart = Instance.new("Part")
- newpart.Shape = Enum.PartType.Cylinder
- local sizex = thing.Size.Y * themesh.Scale.Y----these don't match; that's on purpose
- local sizey = thing.Size.X * themesh.Scale.X----these don't match; that's on purpose, as the sizing for a cylinder part and the sizing for a normal part with a cylinder mesh is different
- local sizez = thing.Size.Z * themesh.Scale.Z
- newpart.Size = Vector3.new(sizex, sizey, sizez)
- newpart.CFrame = thing.CFrame * CFrame.new(themesh.Offset) * CFrame.Angles(0, 0, math.pi / 2)
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Name = thing.Name
- newpart.Parent = newmodel
- elseif themesh.ClassName == "SpecialMesh" then
- if themesh.MeshType == Enum.MeshType.Brick or themesh.MeshType == Enum.MeshType.Torso then
- local newpart = Instance.new("Part")
- newpart.Size = thing.Size * themesh.Scale
- newpart.CFrame = thing.CFrame * CFrame.new(themesh.Offset)
- newpart.Name = thing.Name
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = newmodel
- elseif themesh.MeshType == Enum.MeshType.Cylinder then
- local newpart = Instance.new("Part")
- newpart.Shape = Enum.PartType.Cylinder
- newpart.Size = thing.Size * themesh.Scale
- newpart.CFrame = thing.CFrame * CFrame.new(themesh.Offset)
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Name = thing.Name
- newpart.Parent = newmodel
- elseif themesh.MeshType == Enum.MeshType.FileMesh then
- local idnumber = string.match(themesh.MeshId, '%d+$') --gets all the numbers at the end of the meshid (the asset id)
- if idnumber == nil or (string.match(themesh.MeshId, 'rbxasset') and not string.match(themesh.MeshId, 'rbxassetid')) then
- idnumber = themesh.MeshId --if code gets here, then mesh id is not an actual asset number (it's somehting like rbxasset://fonts/slingshot.mesh)
- end
- local newpart = game.ServerStorage.meshpartsforscanner:FindFirstChild(idnumber)
- if newpart then
- newpart = newpart:Clone()
- newpart.Size = newpart.Size * themesh.Scale
- newpart.CFrame = thing.CFrame * CFrame.new(themesh.Offset)
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Name = thing.Name
- newpart.Parent = newmodel
- else
- newpart = Instance.new('Part')
- newpart.Size = Vector3.new(.5, .5, .5)
- newpart.CFrame = scanlocation
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Name = thing.Name
- newpart.Parent = newmodel
- if not makedotinfo[newmodel] then
- preparetable(newmodel)
- end
- makedotinfo[newmodel].message = 'Some or all of this model was not scanned properly'
- end
- elseif themesh.MeshType == Enum.MeshType.Head then
- local newpart = script.Parent.Parent.Parent.storage.meshparthead:Clone()
- local sizex
- local sizey = thing.Size.Y * themesh.Scale.Y----these don't match; that's on purpose, as the sizing for a cylinder part and the sizing for a normal part with a cylinder mesh is different
- local sizez
- if thing.Size.X < thing.Size.Z or thing.Size.X == thing.Size.Z then
- sizex = thing.Size.X * themesh.Scale.X
- sizez = thing.Size.X * themesh.Scale.X
- elseif thing.Size.Z < thing.Size.X then
- sizex = thing.Size.Z * themesh.Scale.Z
- sizez = thing.Size.Z * themesh.Scale.Z
- else
- local scale
- if themesh.Scale.X < themesh.Scale.Z then
- scale = themesh.Scale.X
- elseif themesh.Scale.Z < themesh.Scale.X then
- scale = themesh.Scale.Z
- else
- scale = themesh.Scale.X
- end
- sizex = thing.Size.X * scale
- sizez = thing.Size.X * scale
- end
- newpart.Size = Vector3.new(sizex, sizey, sizez)
- newpart.CFrame = thing.CFrame * CFrame.new(themesh.Offset)
- newpart.Name = thing.Name
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = newmodel
- elseif themesh.MeshType == Enum.MeshType.Sphere then
- local newpart = script.Parent.Parent.Parent.storage.meshpartball:Clone()
- newpart.Size = thing.Size * themesh.Scale
- newpart.CFrame = thing.CFrame * CFrame.new(themesh.Offset)
- newpart.Name = thing.Name
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = newmodel
- --elseif themesh.MeshType == Enum.MeshType.Torso then --this is paired with meshtype==brick
- elseif themesh.MeshType == Enum.MeshType.Wedge then
- local newpart = Instance.new('WedgePart')
- newpart.Size = thing.Size * themesh.Scale
- newpart.CFrame = thing.CFrame * CFrame.new(themesh.Offset)
- newpart.Name = thing.Name
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = newmodel
- end --if themesh.MeshType
- end --if not themesh
- end --if thing:IsA('BasePart')
- for _, v in pairs(thing:GetChildren()) do
- changemeshesformodel(v, newmodel)
- end
- end
- --this is not meant for scanning individual objects in a model
- function changemeshesforpart(thing) --thing is the object whose children we are checking; it is expected that this 'thing' has no children of type 'BasePart' (blocks)
- --note: the new brick will be returned with an unmodified position, and if no meshes were found in the brick, a clone of the original brick will be returned
- if thing:IsA('BasePart') then --if it's not a basepart, then it doesn't matter if there is a mesh child. We will just continue looking through its children, as one of the children could be a basepart
- local themesh
- --I am doing this for loop because the part could have multiple meshes; if a part has multiple meshes, it uses the most recent mesh, and the most recent mesh is the last one to be iterated through in "in pairs"
- for _, v in pairs(thing:GetChildren()) do
- if v and v:IsA("DataModelMesh") then
- themesh = v
- end --if v and v:IsA('DataModelMesh')
- end --for _, v in pairs
- if not themesh then
- local newpart = thing:Clone()
- newpart.Name = thing.Name
- newpart:ClearAllChildren()
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = game.Workspace
- return newpart
- elseif themesh.ClassName == "BlockMesh" then
- local newpart = Instance.new("Part")
- newpart.Size = thing.Size * themesh.Scale
- newpart.CFrame = scanlocation
- newpart.Name = thing.Name
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = game.Workspace
- return newpart
- elseif themesh.ClassName == "CylinderMesh" then
- local newpart = Instance.new("Part")
- newpart.Shape = Enum.PartType.Cylinder
- local sizex = thing.Size.Y * themesh.Scale.Y----these don't match; that's on purpose
- local sizey = thing.Size.X * themesh.Scale.X----these don't match; that's on purpose, as the sizing for a cylinder part and the sizing for a normal part with a cylinder mesh is different
- local sizez = thing.Size.Z * themesh.Scale.Z
- newpart.Size = Vector3.new(sizex, sizey, sizez)
- newpart.CFrame = scanlocation
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Name = thing.Name
- newpart.Parent = game.Workspace
- return newpart
- elseif themesh.ClassName == "SpecialMesh" then
- if themesh.MeshType == Enum.MeshType.Brick or themesh.MeshType == Enum.MeshType.Torso then
- local newpart = Instance.new("Part")
- newpart.Size = thing.Size * themesh.Scale
- newpart.CFrame = scanlocation
- newpart.Name = thing.Name
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = game.Workspace
- return newpart
- elseif themesh.MeshType == Enum.MeshType.Cylinder then
- local newpart = Instance.new("Part")
- newpart.Shape = Enum.PartType.Cylinder
- newpart.Size = thing.Size * themesh.Scale
- newpart.CFrame = scanlocation
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Name = thing.Name
- newpart.Parent = game.Workspace
- return newpart
- elseif themesh.MeshType == Enum.MeshType.FileMesh then
- local idnumber = string.match(themesh.MeshId, '%d+$') --gets all the numbers at the end of the meshid (the asset id)
- if idnumber == nil or (string.match(themesh.MeshId, 'rbxasset') and not string.match(themesh.MeshId, 'rbxassetid')) then
- idnumber = themesh.MeshId --if code gets here, then mesh id is not an actual asset number (it's somehting like rbxasset://fonts/slingshot.mesh)
- end
- local newpart = game.ServerStorage.meshpartsforscanner:FindFirstChild(idnumber)
- if newpart then
- newpart = newpart:Clone()
- newpart.Size = newpart.Size * themesh.Scale
- newpart.CFrame = scanlocation
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Name = thing.Name
- newpart.Parent = game.Workspace
- return newpart
- else
- newpart = Instance.new('Part')
- newpart.Size = Vector3.new(.5, .5, .5)
- newpart.CFrame = scanlocation
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Name = thing.Name
- newpart.Parent = game.Workspace
- if not makedotinfo[newpart] then
- preparetable(newpart)
- end
- makedotinfo[newpart].message = 'This mesh could not be scanned'
- return newpart
- end
- elseif themesh.MeshType == Enum.MeshType.Head then
- local newpart = script.Parent.Parent.Parent.storage.meshparthead:Clone()
- local sizex
- local sizey = thing.Size.Y * themesh.Scale.Y----these don't match; that's on purpose, as the sizing for a cylinder part and the sizing for a normal part with a cylinder mesh is different
- local sizez
- if thing.Size.X < thing.Size.Z or thing.Size.X == thing.Size.Z then
- sizex = thing.Size.X * themesh.Scale.X
- sizez = thing.Size.X * themesh.Scale.X
- elseif thing.Size.Z < thing.Size.X then
- sizex = thing.Size.Z * themesh.Scale.Z
- sizez = thing.Size.Z * themesh.Scale.Z
- else
- local scale
- if themesh.Scale.X < themesh.Scale.Z then
- scale = themesh.Scale.X
- elseif themesh.Scale.Z < themesh.Scale.X then
- scale = themesh.Scale.Z
- else
- scale = themesh.Scale.X
- end
- sizex = thing.Size.X * scale
- sizez = thing.Size.X * scale
- end
- newpart.Size = Vector3.new(sizex, sizey, sizez)
- newpart.CFrame = scanlocation
- newpart.Name = thing.Name
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = game.Workspace
- return newpart
- elseif themesh.MeshType == Enum.MeshType.Sphere then
- local newpart = script.Parent.Parent.Parent.storage.meshpartball:Clone()
- newpart.Size = thing.Size * themesh.Scale
- newpart.CFrame = scanlocation
- newpart.Name = thing.Name
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = game.Workspace
- return newpart
- --elseif themesh.MeshType == Enum.MeshType.Torso then --this is paired with meshtype==brick
- elseif themesh.MeshType == Enum.MeshType.Wedge then
- local newpart = Instance.new('WedgePart')
- newpart.Size = thing.Size * themesh.Scale
- newpart.CFrame = scanlocation
- newpart.Name = thing.Name
- newpart.Anchored = true
- newpart.CanCollide = false
- newpart.Transparency = 1
- newpart.Parent = game.Workspace
- return newpart
- end --if themesh.MeshType
- end --if not themesh
- end --if thing:IsA('BasePart')
- end
- --thing will be a part or model
- function preparetable(thing) --this is used to prepare the table "makedotinfo" used in the function makedot; if we didn't do it here and did it function makedot, it would check the table several times
- makedotinfo[thing] = {}
- local holder = script.Parent.holder:Clone()
- holder.Parent = script.Parent.SurfaceGui.scanimages
- makedotinfo[thing].holder = holder
- end
- function makedot(x, y, distance, depth, thing)
- local x = (x * dotsize) - dotsize
- local y = (y * dotsize) - dotsize
- local dot = script.Parent.dot:Clone()
- local brightnesslimit = 200 --this + 15 is the brightest the gui will be allowed to go
- local brightnessmodifier = (depth - (depth * .1)) / brightnesslimit
- local thecolor = distance / brightnessmodifier
- thecolor = thecolor + 15
- if thecolor < brightnesslimit then
- dot.BackgroundColor3 = Color3.new(thecolor/255, thecolor/255, thecolor/255)
- else
- dot.BackgroundColor3 = Color3.new(brightnesslimit/255, brightnesslimit/255, brightnesslimit/255)
- end
- dot.Position = UDim2.new(0, x, 0, y)
- dot.Parent = makedotinfo[thing].holder
- end
- function findlargestfaceformodel(model, cframe) --cframe is the CFrame of the model
- local largestface
- local x, y, z
- local c1, c2, c3, c4 --coordinate 1, 2, 3, 4
- local v1, v2, v3 --vertice length 1, 2, 3
- local toparea, leftarea, backarea --area of top face, left face, and back face
- local size = model:GetExtentsSize()
- local position = cframe.Position
- x = position.X - (size.X / 2)
- y = (size.Y / 2) + position.Y
- z = position.Z - (size.Z / 2)
- c1 = Vector3.new(x, y, z) --top left corner (face of brick that is seen from a top down perspective and while brick has no rotation, also the corner made by the front-top-leftside faces)
- c2 = c1 + Vector3.new(0, 0, size.Z) --going over +Z part size from c1
- c3 = c2 - Vector3.new(0, size.Y, 0) --going over -Y part size from c2
- c4 = c3 + Vector3.new(size.X, 0, 0) --going over +X part size from c3
- v1 = (c1 - c2).magnitude
- v2 = (c2 - c3).magnitude
- v3 = (c3 - c4).magnitude
- leftarea = v1 * v2 --note that left and right area are same, top area and bottom area are same, etc
- backarea = v2 * v3
- toparea = v3 * v1
- if leftarea > backarea and leftarea > toparea then
- largestface = "left"
- elseif backarea > leftarea and backarea > toparea then
- largestface = "back"
- elseif toparea > leftarea and toparea > backarea then
- largestface = "top"
- elseif leftarea > backarea or leftarea > toparea then --if code gets here, it means two or more sizes could be the same
- largestface = "left"
- elseif backarea > leftarea or backarea > toparea then
- largestface = "back"
- elseif toparea > leftarea or toparea > backarea then
- largestface = "top"
- else --if code gets here, that means object is perfectly cube shaped
- largestface = "left"
- end
- return largestface
- end
- function findlargestface(part) --not used for models, parts only ; use findlargestfaceformodel for models
- local largestface
- local x, y, z
- local c1, c2, c3, c4 --coordinate 1, 2, 3, 4
- local v1, v2, v3 --vertice length 1, 2, 3
- local toparea, leftarea, backarea --area of top face, left face, and back face
- x = part.Position.X - (part.Size.X / 2)
- y = (part.Size.Y / 2) + part.Position.Y
- z = part.Position.Z - (part.Size.Z / 2)
- c1 = Vector3.new(x, y, z) --top left corner (face of brick that is seen from a top down perspective and while brick has no rotation, also the corner made by the front-top-leftside faces)
- c2 = c1 + Vector3.new(0, 0, part.Size.Z) --going over +Z part size from c1
- c3 = c2 - Vector3.new(0, part.Size.Y, 0) --going over -Y part size from c2
- c4 = c3 + Vector3.new(part.Size.X, 0, 0) --going over +X part size from c3
- v1 = (c1 - c2).magnitude
- v2 = (c2 - c3).magnitude
- v3 = (c3 - c4).magnitude
- leftarea = v1 * v2 --note that left and right area are same, top area and bottom area are same, etc
- backarea = v2 * v3
- toparea = v3 * v1
- if leftarea > backarea and leftarea > toparea then
- largestface = "left"
- elseif backarea > leftarea and backarea > toparea then
- largestface = "back"
- elseif toparea > leftarea and toparea > backarea then
- largestface = "top"
- elseif leftarea > backarea or leftarea > toparea then --if code gets here, it means two or more sizes could be the same
- largestface = "left"
- elseif backarea > leftarea or backarea > toparea then
- largestface = "back"
- elseif toparea > leftarea or toparea > backarea then
- largestface = "top"
- else --if code gets here, that means object is perfectly cube shaped
- largestface = "left"
- end
- return largestface
- end
- function castray(x, y, z, guix, guiy, depth, part) --REMEMBER: the 3D Z is equal to the gui Y
- local startpoint = Vector3.new(x, y, z)
- local direction = Vector3.new(0, -1, 0) --this direction is straight down
- local ray = Ray.new(startpoint, direction * depth)
- local part, pos = game.Workspace:FindPartOnRayWithWhitelist(ray, {part})
- local distance
- --game.Workspace.indicate1.Position = startpoint
- --game.Workspace.indicate2.CFrame = CFrame.new(startpoint + ( (depth + (raycaststartdistance * 2) ) * direction ) )
- if part then
- distance = ( (pos-startpoint).magnitude ) - raycaststartdistance
- makedot(guix, guiy, distance, depth, part)
- end
- end
- function castrayformodel(startpoint, endpoint, guix, guiy, model, modelcframe, depth) --REMEMBER: the 3D Z is equal to the gui Y
- local modelcframe, _ = model:GetBoundingBox()
- local startpoint = modelcframe:toWorldSpace(CFrame.new(startpoint)).Position
- local endpoint = modelcframe:toWorldSpace(CFrame.new(endpoint)).Position
- local direction = (endpoint - startpoint).unit --this direction is straight down
- local ray = Ray.new(startpoint, direction * depth)
- local part, pos = game.Workspace:FindPartOnRayWithWhitelist(ray, {model})
- local distance
- if part then
- distance = ( (pos-startpoint).magnitude ) - raycaststartdistance
- makedot(guix, guiy, distance, depth, model)
- end
- end
- --\/ originalpart could be a tool, model, etc, not just a part
- function doscanforpart(part, itemowner, originalpart) --when this is used, it is expected that 'part' is a basepart and 'part' has no basepart children and 'part' has no meshes in it (use changemeshesforpart function to take care of this)
- part.Anchored = true
- part.CanCollide = false
- part.Transparency = 1
- part.CFrame = scanlocation
- part.Parent = game.Workspace
- local largestface = findlargestface(part)
- local leftpos, toppos, frontpos, xsize, zsize
- local depth
- --NOTE: leftpos, toppos, and frontpos don't represent the actual front, left, and top faces of the brick; the top face of the brick is whatever face of the brick is pointing up after we rotate the brick
- --and so on with the other two faces
- --AND these variables represent the one dimensional (only x, y, or z) position of that face; example, top face position may be something like 10 (as in Y = 10)
- if largestface == "top" then
- leftpos = part.Position.X - (part.Size.X / 2)
- toppos = (part.Size.Y / 2) + part.Position.Y
- frontpos = part.Position.Z - (part.Size.Z / 2)
- xsize = part.Size.X
- zsize = part.Size.Z
- depth = (part.Size.Y) + (raycaststartdistance * 2)
- elseif largestface == "left" then
- part.CFrame = scanlocation * CFrame.Angles(0, 0, -math.pi / 2)
- leftpos = part.Position.X - (part.Size.Y / 2)
- toppos = (part.Size.X / 2) + part.Position.Y
- frontpos = part.Position.Z - (part.Size.Z / 2)
- xsize = part.Size.Y
- zsize = part.Size.Z
- depth = (part.Size.X) + (raycaststartdistance * 2)
- elseif largestface == "back" then
- --NOTE: here, we are actually going to look at the front of the brick (which is opposite the back) but that's fine, front or back will work
- part.CFrame = scanlocation * CFrame.Angles(math.pi / 2, 0, 0)
- leftpos = part.Position.X - (part.Size.X / 2)
- toppos = (part.Size.Z / 2) + part.Position.Y
- frontpos = part.Position.Z - (part.Size.Y / 2)
- xsize = part.Size.X
- zsize = part.Size.Y
- depth = (part.Size.Z) + (raycaststartdistance * 2)
- end
- local increment = increment
- if xsize * zsize <= 10 then
- increment = increment / 2
- end
- if not makedotinfo[part] then
- preparetable(part)
- end
- local startposx = leftpos
- local startposy = toppos + raycaststartdistance
- local startposz = frontpos
- local guix = 0
- local guiy = 0
- local xpos = 0
- local zpos = 0
- for i = 0, zsize / increment do
- guiy = guiy + 1
- guix = 0
- for j = 0, xsize / increment do
- guix = guix + 1
- local addtox = j * increment
- local addtoz = i * increment
- castray(startposx + addtox, startposy, startposz + addtoz, guix, guiy, depth, part)
- end
- end
- local dotholder = makedotinfo[part].holder
- generalinfoarray[dotholder] = {}
- local message = makedotinfo[part].message
- if message then
- generalinfoarray[dotholder].message = message
- if part.Name == transparentname then
- generalinfoarray[dotholder].message = generalinfoarray[dotholder].message .. '\nNote that this part is transparent'
- end
- elseif part.Name == transparentname then
- generalinfoarray[dotholder].message = 'Note that this part is transparent'
- end
- generalinfoarray[dotholder].itemowner = itemowner
- generalinfoarray[dotholder].scannedthing = originalpart
- makedotinfo[part] = nil
- end
- function anchorandhidechildren(thing)
- if thing and thing:IsA("BasePart") then
- thing.Anchored = true
- thing.Transparency = 1
- thing.CanCollide = false
- end
- for _, v in pairs(thing:GetChildren()) do
- anchorandhidechildren(v)
- end
- end
- function doscanformodel(model, itemowner, originalmodel) --when this is used, it is expected that 'model' is a model, there is at least one basepart in the model, and none of the baseparts are affected by meshes (use changemeshesformodel for this)
- anchorandhidechildren(model)
- giveprimarypart(model, model)
- model:SetPrimaryPartCFrame(scanlocation)
- model.PrimaryPart = nil
- model.Parent = game.Workspace
- if not makedotinfo[model] then
- preparetable(model)
- end
- local modelcframe, size = model:GetBoundingBox()
- --model:SetPrimaryPartCFrame( scanlocation * (modelcframe - modelcframe.p) )
- --model.Parent = game.Workspace
- local largestface = findlargestfaceformodel(model, modelcframe)
- local leftpos, toppos, frontpos, xsize, zsize
- local position = modelcframe.p
- local startposx, startposy, startposz --where the first raycasts will begin
- local firstdirection, seconddirection --used for scanning the model up to down left to right
- local extradistance --this is used so the raycast doesn't start right against the brick; see the variable "raycaststartdistance" at top of page; this will be a Vector3
- --NOTE: leftpos, toppos, and frontpos don't represent the actual front, left, and top faces of the brick; the top face of the brick is whatever face of the brick is pointing up after we rotate the brick
- --and so on with the other two faces
- --AND these variables represent the one dimensional (only x, y, or z) position of that face; example, top face position may be something like 10 (as in Y = 10)
- if largestface == "top" then
- startposx = size.X / -2
- startposy = size.Y / 2
- startposz = size.Z / -2
- firstdirection = Vector3.new(0, 0, 1)
- seconddirection = Vector3.new(1, 0, 0)
- extradistance = Vector3.new(0, 1, 0) * raycaststartdistance
- elseif largestface == "left" then
- startposx = size.X / -2
- startposy = size.Y / 2
- startposz = size.Z / -2
- firstdirection = Vector3.new(0, -1, 0)
- seconddirection = Vector3.new(0, 0, 1)
- extradistance = Vector3.new(-1, 0, 0) * raycaststartdistance
- elseif largestface == "back" then
- startposx = size.X / -2
- startposy = size.Y / 2
- startposz = size.Z / 2
- firstdirection = Vector3.new(0, -1, 0)
- seconddirection = Vector3.new(1, 0, 0)
- extradistance = Vector3.new(0, 0, 1) * raycaststartdistance
- end
- local guix = 0
- local guiy = 0
- local size1 = (size * firstdirection).magnitude --size1 and size2 is getting the first and second size we need (as in the X, Y, or Z size of the model)
- local size2 = (size * seconddirection).magnitude --example of how this works ( (3, 4, 7) * (1, 0, 0) ).magnitude = 3
- local depth = (extradistance.unit * size).magnitude --this is getting the size of the brick/model that the ray is going through; if we are raycasting into the top side of the brick, then depth would be Size.Y
- local increment = increment
- if size1 * size2 <= 10 then
- increment = increment / 2
- end
- local startpos = Vector3.new(startposx, startposy, startposz)
- for i = 0, size1 / increment do
- wait()
- guiy = guiy + 1
- guix = 0
- for j = 0, size2 / increment do
- guix = guix + 1
- local addtostartpos1 = i * increment * firstdirection--this variable is a Vector3
- local addtostartpos2 = j * increment * seconddirection--this variable is a Vector3
- local raycastfromhere = startpos + addtostartpos1 + addtostartpos2 + extradistance
- local raycasttohere = raycastfromhere - ( (size * extradistance.unit) + (extradistance * 2) )
- castrayformodel(raycastfromhere, raycasttohere, guix, guiy, model, modelcframe, depth)
- --raycastfromhere: start position of ray cast
- --raycasttohere: end position of ray cast
- --guix: x position of where on the gui a dot will be made (won't represent exact location, as this number will be multiplied and stuff)
- --guiy: y position of where on the gui a dot will be made (won't represent exact location, as this number will be multiplied and stuff)
- --model: the model
- --modelcframe: the cframe of the model
- --depth: depth is getting the size of the brick/model that the ray is going through; if we are raycasting into the top side of the brick, then depth would be Size.Y
- --go up about 22 lines to see this variable or search script for "local depth ="
- end
- end
- local dotholder = makedotinfo[model].holder
- generalinfoarray[dotholder] = {}
- local message = makedotinfo[model].message
- if message then
- generalinfoarray[dotholder].message = message
- end
- generalinfoarray[dotholder].itemowner = itemowner
- generalinfoarray[dotholder].scannedthing = originalmodel
- makedotinfo[model] = nil
- end
- function modifymeshpartsize(part)
- local minarea = 4
- local largestface = findlargestface(part)
- if largestface == 'left' then --Z and Y
- local area = part.Size.Z * part.Size.Y
- if area < minarea then
- part.Size = part.Size * (minarea / area)
- end
- elseif largestface == 'back' then --x and y
- local area = part.Size.X * part.Size.Y
- if area < minarea then
- part.Size = part.Size * (minarea / area)
- end
- else --top z and x
- local area = part.Size.Z * part.Size.X
- if area < minarea then
- part.Size = part.Size * (minarea / area)
- end
- end
- end
- function getnumoftangiblechildren(item)
- local num = 0
- for _, v in pairs(item:GetDescendants()) do
- if v:IsA('BasePart') then-- and (v.Transparency < .95 or ignoretransparentitems == false) then
- num = num + 1
- end
- end
- return num
- end
- function checkformeshchildren(item) --checks to see if item or any descendants of item are a mesh
- if item:IsA('DataModelMesh') then
- return true
- end
- for _, v in pairs(item:GetDescendants()) do
- if v:IsA('DataModelMesh') then
- return true
- end
- end
- end
- function checkfortransparentchildren(item) --checks to see if item or any descendant of item are transparent
- if item and item:IsA('BasePart') and item.Name == transparentname > .95 then
- return true
- end
- for _, v in pairs(item:GetDescendants()) do
- if v and v:IsA('BasePart') and v.Name == transparentname then
- return true
- end
- end
- end
- function doscanforthing(oldthing)
- local itemowner
- local thing
- if oldthing.Parent.ClassName == 'Model' then
- local playerwhoownsitem = game.Players:GetPlayerFromCharacter(oldthing.Parent)
- if playerwhoownsitem then
- itemowner = playerwhoownsitem.Backpack
- else
- itemowner = oldthing.Parent
- end
- else
- itemowner = oldthing.Parent
- end
- thing = oldthing:Clone()
- if thing and thing:IsA('BasePart') and thing.Transparency > .95 and ignoretransparentitems == true then
- thing.Name = transparentname
- end
- if ignoretransparentitems == true then
- for _, v in pairs(thing:GetDescendants()) do
- if v and v:IsA('BasePart') and v.Transparency > .95 then
- v.Name = transparentname
- end
- end
- end
- local num = getnumoftangiblechildren(thing)
- print(num)
- if num == 0 then
- if thing:IsA('BasePart') then
- if checkformeshchildren(thing) then
- local newthing = changemeshesforpart(thing)
- thing:Destroy()
- thing = newthing
- newthing = nil
- doscanforpart(thing, itemowner, oldthing)
- thing:Destroy()
- else
- doscanforpart(thing, itemowner, oldthing)
- thing:Destroy()
- end
- else
- -- script.Parent.SurfaceGui.interface.message.Text = 'nothing physical to scan'
- -- script.Parent.SurfaceGui.interface.message.Visible = true
- -- wait(2)
- -- script.Parent.SurfaceGui.interface.message.Text = ''
- -- script.Parent.SurfaceGui.interface.message.Visible = false
- end
- elseif num == 1 then
- print('==1')
- if thing:IsA('BasePart') then
- local model = Instance.new('Model')
- thing.Parent = model
- if checkformeshchildren(model) or (checkfortransparentchildren(thing) and ignoretransparentitems == true) then
- local newmodel = Instance.new('Model')
- changemeshesformodel(model, newmodel)
- model:Destroy()
- doscanformodel(newmodel, itemowner, oldthing)
- newmodel:Destroy()
- else
- doscanformodel(model, itemowner, oldthing)
- model:Destroy()
- end
- else
- for _, v in pairs(thing:GetDescendants()) do
- if v:IsA('BasePart') then
- if checkformeshchildren(v) then
- local newpart = changemeshesforpart(v)
- doscanforpart(newpart, itemowner, oldthing)
- thing:Destroy()
- newpart:Destroy()
- else
- doscanforpart(v, itemowner, oldthing)
- thing:Destroy()
- end
- break
- end
- end
- end
- elseif num > 1 then
- if thing.ClassName == 'Model' then
- if checkformeshchildren(thing) or (checkfortransparentchildren(thing) and ignoretransparentitems == true) then
- local newmodel = Instance.new('Model')
- changemeshesformodel(thing, newmodel)
- thing:Destroy()
- doscanformodel(newmodel, itemowner, oldthing)
- newmodel:Destroy()
- else
- doscanformodel(thing, itemowner, oldthing)
- thing:Destroy()
- end
- else
- local model = Instance.new('Model')
- thing.Parent = model
- if checkformeshchildren(model) or (checkfortransparentchildren(thing) and ignoretransparentitems == true) then
- local newmodel = Instance.new('Model')
- changemeshesformodel(model, newmodel)
- model:Destroy()
- doscanformodel(newmodel, itemowner, oldthing)
- newmodel:Destroy()
- else
- doscanformodel(model, itemowner, oldthing)
- model:Destroy()
- end
- end
- end
- end
- function makescannerrotate()
- hinge.TargetAngle = hinge.TargetAngle * -1
- wait(1)
- hinge.AngularSpeed = .2
- wait(1)
- hinge.AngularSpeed = 1.6
- end
- --game.Workspace.testevent.OnServerEvent:Connect(function(player, arg)
- -- print('got it')
- -- local newmodel = Instance.new('Model', workspace)
- -- changemeshesformodel(arg.Parent, newmodel)
- -- newmodel.PrimaryPart = newmodel.Part
- -- newmodel:SetPrimaryPartCFrame(CFrame.new(game.Workspace.imalex4.Head.Position + Vector3.new(0, 6, 0)))
- -- for _, v in pairs(newmodel:GetChildren()) do
- -- v.Transparency = 0
- -- end
- --end)
- --game.Workspace.testevent.OnServerEvent:Connect(function(player, arg)
- -- print('got it')
- --
- -- local newpart = changemeshesforpart(arg)
- -- newpart.Position = game.Workspace.imalex4.Head.Position + Vector3.new(0, 3, 0)
- -- newpart.Transparency = 0
- -- newpart.Anchored = true
- --end)
- script.Parent.Parent.RemoteEvent.OnServerEvent:Connect(function(player, args)
- if player and player.Character and player.Character.Parent and player.Character.Humanoid.Health > 0 and (player.Character.Head.Position - script.Parent.Position).magnitude <= clickdistance then
- ----//////////// if you only want certain players to be able to use the body scanner, modify the line below
- if player then
- if args == 'directthru' then
- if not debounces[args] then
- debounces[args] = true
- script.Parent.Parent.Parent.simplescreen1.SurfaceGui.TextLabel.Text = 'GO THRU'
- script.Parent.Parent.Parent.simplescreen2.SurfaceGui.TextLabel.Text = 'GO THRU'
- script.Parent.Parent.Parent.simplescreen1.SurfaceGui.TextLabel.TextColor3 = Color3.new(0, 255, 0)
- script.Parent.Parent.Parent.simplescreen2.SurfaceGui.TextLabel.TextColor3 = Color3.new(0, 255, 0)
- script.Parent.Parent.Parent.simplescreen1.Sound:Play()
- wait(1.9)
- script.Parent.Parent.Parent.simplescreen1.SurfaceGui.TextLabel.Text = '-WAIT-'
- script.Parent.Parent.Parent.simplescreen2.SurfaceGui.TextLabel.Text = '-WAIT-'
- script.Parent.Parent.Parent.simplescreen1.SurfaceGui.TextLabel.TextColor3 = Color3.new(255, 0, 0)
- script.Parent.Parent.Parent.simplescreen2.SurfaceGui.TextLabel.TextColor3 = Color3.new(255, 0, 0)
- debounces[args] = false
- else
- script.Parent.Parent.Parent.simplescreen1.Sound:Play()
- end
- elseif args == 'scan' then
- if not debounces[args] and not debounces['previousimage'] and not debounces['nextimage'] and not debounces['doscan'] then
- debounces[args] = true
- debounces['doscan'] = true
- debounces['previousimage'] = true
- debounces['nextimage'] = true
- for _, v in pairs(gui.scanimages:GetChildren()) do
- v:Destroy()
- wait()
- end
- scanimagesindex = 0
- interface.message.Text = ''
- interface.scannumber.Text = ''
- interface.message.Visible = false
- interface.take.Text = 'Confiscate'
- debounces['doscan'] = false
- debounces['previousimage'] = false
- debounces['nextimage'] = false
- debounces[args] = false
- end
- if not debounces[args] then
- debounces[args] = true
- numofscanimages = 0
- numofpeopleinscanner = 0
- interface.scannumber.Text = ''
- interface.message.Text = 'Scanning in progress'
- interface.message.Visible = true
- makescannerrotate()
- timeofscanstart = tick()
- for _, v in pairs(peopleinscanner) do
- numofpeopleinscanner = numofpeopleinscanner + 1
- --#peopleinscanner doesn't work here
- end
- if numofpeopleinscanner == 0 or numofpeopleinscanner > 1 then
- wait(1.8 - (tick() - timeofscanstart) )
- interface.message.Text = 'no one in scanner or multiple people in scanner'
- interface.message.Visible = true
- wait(2)
- interface.message.Visible = false
- interface.message.Text = ''
- else --numofpeopleinscanner == 1
- debounces['previousimage'] = true
- debounces['nextimage'] = true
- debounces['clear'] = true
- scanimagesindex = 0
- makedotinfo = {}
- generalinfoarray = {}
- for _, v in pairs(gui.scanimages:GetChildren()) do
- v:Destroy()
- wait()
- end
- local character
- for i, v in pairs(peopleinscanner) do
- character = i --this is correct, character is not v; peopleinscanner is a map array
- --at this point in the code, there should only be one person in the array
- end
- local player = game.Players:GetPlayerFromCharacter(character)
- if not player then
- for _, v in pairs(character:GetChildren()) do
- if v and v:IsA('BackpackItem') then
- doscanforthing(v)
- end
- end
- else
- for _, v in pairs(player.Backpack:GetChildren()) do
- if v then
- doscanforthing(v)
- end
- end
- for _, v in pairs(character:GetChildren()) do
- if v and v:IsA('BackpackItem') then
- doscanforthing(v)
- end
- end
- end
- scanimages = gui.scanimages:GetChildren()
- for _, v in pairs(scanimages) do
- numofscanimages = numofscanimages + 1
- end
- wait(1.8 - (tick() - timeofscanstart) )
- if numofscanimages == 0 then
- interface.message.Text = 'This person has no items'
- interface.message.Visible = true
- wait(1.5)
- else
- if numofscanimages > 1 then
- interface.scannumber.Text = '1 of ' .. numofscanimages
- end
- scanimages[1].Visible = true
- scanimagesindex = 1
- end
- local message = generalinfoarray[ scanimages[scanimagesindex] ].message
- if message then
- interface.message.Text = message
- interface.message.Visible = true
- else
- interface.message.Visible = false
- interface.message.Text = ''
- end
- debounces['clear'] = false
- debounces['previousimage'] = false
- debounces['nextimage'] = false
- end
- debounces[args] = false
- end
- elseif args == 'previousimage' then
- if not debounces[args] and scanimagesindex ~= 0 then
- debounces[args] = true
- debounces['doscan'] = true
- if scanimagesindex > 1 then
- interface.message.Visible = false
- interface.message.Text = ''
- scanimages[scanimagesindex].Visible = false
- scanimagesindex = scanimagesindex - 1
- scanimages[scanimagesindex].Visible = true
- local message = generalinfoarray[ scanimages[scanimagesindex] ].message
- if message then
- interface.message.Text = message
- interface.message.Visible = true
- end
- interface.scannumber.Text = scanimagesindex .. ' of ' .. numofscanimages
- if generalinfoarray[ scanimages[scanimagesindex] ].currentitemholder then
- interface.take.Text = 'Return Item'
- else
- interface.take.Text = 'Confiscate'
- end
- end
- debounces['doscan'] = false
- debounces[args] = false
- end
- elseif args == 'nextimage' then
- if not debounces[args] and scanimagesindex ~= 0 then
- debounces[args] = true
- debounces['doscan'] = true
- if scanimagesindex < numofscanimages then
- interface.message.Visible = false
- interface.message.Text = ''
- scanimages[scanimagesindex].Visible = false
- scanimagesindex = scanimagesindex + 1
- scanimages[scanimagesindex].Visible = true
- local message = generalinfoarray[ scanimages[scanimagesindex] ].message
- if message then
- interface.message.Text = message
- interface.message.Visible = true
- end
- interface.scannumber.Text = scanimagesindex .. ' of ' .. numofscanimages
- if generalinfoarray[ scanimages[scanimagesindex] ].currentitemholder then
- interface.take.Text = 'Return Item'
- else
- interface.take.Text = 'Confiscate'
- end
- end
- debounces['doscan'] = false
- debounces[args] = false
- end
- elseif args == 'clear' then
- if not debounces[args] and not debounces['previousimage'] and not debounces['nextimage'] and not debounces['doscan'] then
- debounces[args] = true
- debounces['doscan'] = true
- debounces['previousimage'] = true
- debounces['nextimage'] = true
- for _, v in pairs(gui.scanimages:GetChildren()) do
- v:Destroy()
- wait()
- end
- scanimagesindex = 0
- interface.message.Text = ''
- interface.scannumber.Text = ''
- interface.message.Visible = false
- interface.take.Text = 'Confiscate'
- debounces['doscan'] = false
- debounces['previousimage'] = false
- debounces['nextimage'] = false
- debounces[args] = false
- end
- elseif args == 'takeorreturn' then
- if not debounces[args] then
- debounces[args] = true
- local currentscanimage = scanimages[scanimagesindex]
- if generalinfoarray[currentscanimage].currentitemholder and generalinfoarray[currentscanimage].currentitemholder.Parent == player and generalinfoarray[currentscanimage].itemowner then
- generalinfoarray[currentscanimage].scannedthing.Parent = generalinfoarray[currentscanimage].itemowner
- generalinfoarray[currentscanimage].currentitemholder = nil
- interface.take.Text = 'Confiscate'
- elseif generalinfoarray[currentscanimage].itemowner and not generalinfoarray[currentscanimage].currentitemholder then
- generalinfoarray[currentscanimage].scannedthing.Parent = player.Backpack
- generalinfoarray[currentscanimage].currentitemholder = player.Backpack
- interface.take.Text = 'Return Item'
- end
- debounces[args] = false
- end
- end --if args ==
- end
- end
- end)
- script.Parent.Parent.Parent.touchpart.Touched:Connect(function(part)
- if part and part.Name == 'Head' and part.Parent and part.Parent:FindFirstChild('Humanoid') and not peopleinscanner[part.Parent] then
- peopleinscanner[part.Parent] = true
- end
- end)
- while true do
- wait(1)
- for i, v in pairs(peopleinscanner) do --NOTE look at i, not v; v will always just be true or nil, as this table is a map
- if not i or not i.Parent or not i:FindFirstChild('Head') or ( (i.Head.Position - script.Parent.Parent.Parent.touchpart.Position).magnitude ) > 3 then
- print('gone')
- peopleinscanner[i] = nil
- end
- end
- end
- --doscanforthing(game.Workspace.p1)
- --doscanforthing(game.Workspace.testmodel)
Advertisement
Add Comment
Please, Sign In to add comment