Quick actions

cmd+k|ctrl+k

Navigation

Languages

crystal pvp v1.3

Snippet info

Language

Lua

Visibility

public

Author

lazarovalkyrie

Created

2024-05-03T03:27:50.349927Z

Updated

2024-07-15T20:06:50.524357Z

-- Made by LazaroValkyrie
-- Crystal PvP

if game:GetService("RunService"):IsStudio() then
	owner = nil
end

local block_size = 2.5
local y_offset = Vector3.new(0,-block_size/2,0)
local hitbox_allowance = Vector3.new(0.05,0.05,0.05)
local BlockVector3 = Vector3.new(block_size,block_size,block_size)

local config = {
	range = 5,
	place_crystal = true,
	place_support_block = true,
	attack_placed_crystals = false,
	surround_speed = 1/10,
	auto_surround = false,
	check_speed = 1/20,
	place_speed = 0,
	crystal_speed = 1/20,
	max_self_damage = 8*5,
	minimum_target_damage = 9*5,
	minimum_health = 0, -- 4*5
	damage_balance = 1.4,
	target_range = 10,
	target_mode = 'damage', -- damage
	fast_place = true, -- uses cached placements
	smart_target = true, -- doesn't reset cache if player is the same
	cache_limit = 20,
	post_death_cache_limit = 20, -- attacks after death (better with smart target)
	fast_calc = false, -- can miss better places
	fast_calc_limit = 3,
	auto_crystal = false,
	check_immunity = false, -- player is immune for 0.5 seconds when getting hurt
	check_immunity_self_damage = true,
	--cev = false, -- place obsidian on head, place crystal, destroy obsidian, destroy crystal
	attack_self = false,
	no_particles = true,
	instant_crystal_render = true, -- shows crystal when instant auto crystal
	lfx = false,
	debug = false,
	classic_explosion = true,
}

local local_entities = {}

local core = {}
core.util = {}
core.blockutil = {}
core.crystalutil = {}
core.entityutil = {}
core.storageutil = {}
core.plr = owner

core.storageutil.get = function(name,no_set)
	if not _G['cpvp server'] then
		_G['cpvp server'] = {}
	end
	if not _G['cpvp server'][name] and not no_set then
		_G['cpvp server'][name] = {}
	end
	return _G['cpvp server'][name]
end

core.storageutil.set = function(i,v)
	if not _G['cpvp server'] then
		_G['cpvp server'] = {}
	end
	_G['cpvp server'][i] = v
	return _G['cpvp server'][i]
end

core.util.clean_up = function()
	coroutine.wrap(function()
		for pos,block in pairs(core.storageutil.get('block_reference')) do
			if not block then
				core.storageutil.get('block_reference')[pos] = nil
			end
		end
		for model,box in pairs(core.storageutil.get('hitboxes')) do
			if model and not box then
				core.storageutil.get('hitboxes')[model] = nil
				if model then
					model:Destroy()
				end
			end
		end
		for entity,_type in pairs(core.storageutil.get('entities')) do
			if entity and not core.entityutil.in_world(entity) then
				core.entityutil.remove_entity(entity)
			end
		end
		for main,data in pairs(core.storageutil.get('anims')) do
			if main and not core.entityutil.in_world(main) then
				core.storageutil.get('anims')[main] = nil
				core.util.destroy(main)
			end
		end
	end)()
end

local ls_anim = os.clock()

local function get_char()
	local char = {}
	if game:GetService("RunService"):IsStudio() then
		char.model = workspace.TemplateR6
	else
		char.model = core.plr.Character
	end

	if char.model then
		char.head = char.model:FindFirstChild("Head")
		char.root = char.model:FindFirstChild("HumanoidRootPart") or char.model:FindFirstChild("Torso")
		char.hum = char.model:FindFirstChildOfClass("Humanoid")
	end

	return char
end

assert(get_char().hum.RigType == Enum.HumanoidRigType.R6,'Character is not R6.')

core.fx = Instance.new("Folder")
core.fx.Parent = script
script.ChildRemoved:Connect(function(obj)
	if obj == core.fx then
		core.fx = Instance.new("Folder")
		core.fx.Parent = script
	end
end)

core.junk = Instance.new("Folder")
core.junk.Parent = script
script.ChildRemoved:Connect(function(obj)
	if obj == core.junk then
		core.junk = Instance.new("Folder")
		core.junk.Parent = script
	end
end)

core.entityutil.in_world = function(obj)
	if obj and obj:IsDescendantOf(workspace) then
		return true
	end
end

core.entityutil.is_alive = function(model)
	if model and model:IsA("Model") then else return end
	local thum = model:FindFirstChildOfClass("Humanoid")
	local troot = model:FindFirstChild("HumanoidRootPart") or model:FindFirstChild("Torso") or model.PrimaryPart

	if thum and troot and thum.Health > 0 then
		return true
	end
end

core.util.destroy = function(obj)
	pcall(function()
		obj:Destroy()
	end)
end

core.blockutil.create_selection = function(part)
	local selection = Instance.new('SelectionBox')
	selection.Color3 = Color3.new()
	selection.Transparency = 0
	selection.SurfaceColor3 = Color3.new(1,0,0)
	selection.SurfaceTransparency = 0.5
	selection.LineThickness = 0.05
	selection.Adornee = part
	selection.Parent = part

	return selection
end

core.entityutil.get_hitbox = function(model,fake,size,offset,position,perm,parent,visible)
	local troot = model:FindFirstChild("HumanoidRootPart") or model:FindFirstChild("Torso") or model.PrimaryPart

	if troot then else return end

	if fake then
		local hb = core.storageutil.get('hitboxes')[model]
		if hb then
			size = hb.Size
			position = hb.Position
			offset = Vector3.new()
		end
		return {
			box = {
				Size = size or Vector3.new(1,4.5-block_size/2,1)-hitbox_allowance*1.5,
				Position = (position or troot.Position) + (offset or Vector3.new(0,-0.75-block_size/4,0))
			},
		}
	end

	if core.storageutil.get('hitboxes')[model] then
		return {
			box = core.storageutil.get('hitboxes')[model],
			destroy = function()
				core.util.destroy(core.storageutil.get('hitboxes')[model])
				core.storageutil.get('hitboxes')[model] = nil
			end,
		}
	end

	local box = Instance.new("Part")
	box.CanCollide = false
	box.Anchored = true
	box.CanTouch = false
	box.CanQuery = true
	box.Massless = true
	box.Color = Color3.new(1,0,0)
	box.Transparency = 1

	box.Size = size or Vector3.new(1,4.5-block_size/2,1)-hitbox_allowance*1.5

	if perm then
		core.storageutil.get('hitboxes')[model] = box
	end

	box.Position = (position or troot.Position) + (offset or Vector3.new(0,-0.75-block_size/4,0))

	if parent then
		box.Parent = parent
	else
		box.Parent = core.junk
	end

	if visible or config.debug then
		local c = box:Clone()
		core.blockutil.create_selection(c)
		c.Parent = workspace

		game:GetService("Debris"):AddItem(c,3)
	end

	return {
		box = box,
		destroy = function()
			if not perm then
				core.util.destroy(box)
			end
		end,
	}
end

core.entityutil.create_points = function(hb)
	local hb_pos = hb.Position
	local size = hb.Size
	local x,y,z = size.X,size.Y,size.z

	x = 0
	y = 2
	z = 0

	local points = {}

	for _x = 0,x do
		for _y = 0,y do
			for _z = 0,z do
				--local pos = hb.Position+Vector3.new(-size.X/2+_x*size.X/x,-size.Y/2+_y*size.Y/y,-size.Z/2+_z*size.Z/z)
				local pos = hb.Position+Vector3.new(0,-size.Y/2.9+_y*size.Y/y,0)

				--if config.debug then
				--	local p = Instance.new("Part")
				--	p.CanCollide = false
				--	p.Anchored = true
				--	p.CanTouch = false
				--	p.CanQuery = true
				--	p.Transparency = 0
				--	p.Position = pos
				--	p.Size = Vector3.new(0.1,0.1,0.1)
				--	p.Parent = core.junk
				--	game:GetService("Debris"):AddItem(p,30)
				--end

				table.insert(points,pos)
			end
		end
	end

	return points
end

core.util.raycast = function(origin,dir,cancollide,mode,ignore)
	local param = RaycastParams.new()
	param.FilterType = mode or Enum.RaycastFilterType.Exclude
	param.FilterDescendantsInstances = ignore
	param.RespectCanCollide = cancollide or false
	param.IgnoreWater = true

	local ray = workspace:Raycast(origin,dir,param)

	return ray
end

core.entityutil.ray_entity = function(origin,model,ignore,is_test)
	if origin and model then else return end

	is_test = not is_test

	local hb = core.entityutil.get_hitbox(model,true)
	local points = core.entityutil.create_points(hb.box)

	local param = RaycastParams.new()
	--ray_param.RespectCanCollide = false -- default to false
	param.IgnoreWater = true
	param.FilterType = Enum.RaycastFilterType.Include
	param.FilterDescendantsInstances = {
		core.blockutil.get_blocks(),
		core.temp_block
	}
	--[[
	param.FilterDescendantsInstances = {
		model,
		core.storageutil.get('hitboxes'),
		core.entityutil.get_entities(),
		core.fx,
		core.junk,
		ignore
	}
	]]

	local exposure = 0
	for _,pos2 in points do
		local dir = (pos2-origin)
		local dist = dir.Magnitude

		local ray = workspace:Raycast(origin,dir,param)

		if not is_test and config.debug then
			local p = Instance.new("Part")
			p.CanCollide = false
			p.Anchored = true
			p.CanTouch = false
			p.CanQuery = true
			p.Color = Color3.new(1,0,0)
			p.Transparency = 0.5
			p.CFrame = CFrame.new(origin+dir/2,pos2)--*CFrame.fromEulerAnglesXYZ(0,90,0)
			p.Size = Vector3.new(0.1,0.1,dist)
			p.Parent = core.junk
			game:GetService("Debris"):AddItem(p,3)
		end

		if not ray then
			exposure += 1
		end
	end

	exposure = exposure/#points

	return exposure
end

core.entityutil.add_entity = function(model,_type,global)
	core.storageutil.get('entities')[model] = _type
	if not global then
		local_entities[model] = true
	end
end

core.entityutil.remove_entity = function(model)
	core.storageutil.get('anims')[model] = nil
	core.storageutil.get('entities')[model] = nil
	local_entities[model] = nil
	if core.storageutil.get('hitboxes')[model] then
		core.storageutil.get('hitboxes')[model]:Destroy()
		core.storageutil.get('hitboxes')[model] = nil
	end
end

core.entityutil.get_entities = function()
	local t = {}

	for entity,_type in pairs(core.storageutil.get('entities')) do
		table.insert(t,entity)
	end

	return t
end

core.util.scan = function(v)
	if v.Name == 'end crystal' then
		core.entityutil.add_entity(v,'end crystal',true)
		return
	end
	if core.entityutil.is_alive(v) then
		--[[
		local thum = v:FindFirstChildOfClass("Humanoid")
		thum.HealthChanged:Connect(function(new_health)
			if 0 >= new_health then
				core.entityutil.remove_entity(v)
			end
		end)
		]]
		core.entityutil.add_entity(v,'player',true)
	end
end

for i,v in ipairs(workspace:GetDescendants()) do
	core.util.scan(v)
end

workspace.DescendantAdded:Connect(function(v)
	task.wait(0.2)
	core.util.scan(v)
end)

workspace.DescendantRemoving:Connect(function(v)
	if core.storageutil.get('entities')[v] then
		core.entityutil.remove_entity(v)
	end
end)

core.util.in_range = function(block_pos)
	local root_pos = core.blockutil.to_grid(get_char().root.Position)
	local dist = (root_pos-block_pos).Magnitude
	if config.range >= dist then
		return 1
	end
end

core.blockutil.get_sphere = function(pos,range)
	local t = {}

	range = math.floor(range)
	local y_range = 0
	
	for i = 1,range do
		local ray = core.util.raycast(core.blockutil.from_grid(pos),Vector3.new(0,-block_size*i,0),true,Enum.RaycastFilterType.Include,{
			workspace:FindFirstChild('Base'),
			workspace:FindFirstChild('Baseplate'),
			workspace:FindFirstChild('SpawnLocation')
		})
		if ray then
			break
		end
		y_range -= 1
	end

	for x = -range,range do
		for y = y_range,range-1 do
			if math.sqrt(x^2+y^2) > range then
				continue
			end
			for z = -range,range do
				if range >= (pos-pos+Vector3.new(x,y,z)).Magnitude then
					table.insert(t,pos+Vector3.new(x,y,z))
				end
			end
		end
	end
	return t
end

local normal_ids = {
	[0] = Vector3.new(1,0,0), -- right
	[1] = Vector3.new(0,1,0), -- top
	[2] = Vector3.new(0,0,1), -- back
	[3] = Vector3.new(-1,0,0), -- left
	[4] = Vector3.new(0,-1,0), -- bottom
	[5] = Vector3.new(0,0,-1), -- front
}

core.blockutil.to_normal = function(surface)
	return normal_ids[surface]
end

core.blockutil.process_block = function(pos,mode)
	if mode then
		core.storageutil.get('place_process')[pos] = 1
	else
		core.storageutil.get('place_process')[pos] = nil
	end
end

core.blockutil.get_block = function(pos)
	if core.entityutil.in_world(core.storageutil.get('block_reference')[pos]) then
		return core.storageutil.get('block_reference')[pos]
	else
		core.storageutil.get('block_reference')[pos] = nil
	end
end

core.blockutil.get_blocks = function(pos)
	local t = {}
	
	for i,v in pairs(core.storageutil.get('block_reference')) do
		table.insert(t,v)
	end
	
	return t
end

core.blockutil.is_block = function(target)
	local pos = core.blockutil.to_grid(target.Position)

	return core.blockutil.get_block(pos) == target
end

core.blockutil.set_block = function(pos,block)
	core.storageutil.get('block_reference')[pos] = block
end

core.entityutil.get_type = function(model)
	if model then else return end
	
	return core.storageutil.get('entities')[model]
end

core.blockutil.destroy_block = function(block)
	if core.blockutil.is_block(block) then
		core.storageutil.get('block_reference')[core.blockutil.to_grid(block.Position)] = nil
		core.util.destroy(block)
	elseif core.entityutil.get_type(block.Parent) == 'end crystal' then
		core.crystalutil.explode(block.Parent)
	end
end

core.blockutil.mine_block = function(block)
	if block then else return end

	core.blockutil.destroy_block(block)
end

core.blockutil.reset_air = function(pos,temp)
	core.storageutil.get('in_air_cache')[pos] = nil
	if temp then
		return
	end
	for _,surface in pairs(normal_ids) do
		if core.blockutil.get_block(pos+surface) then
			return
		end
		core.storageutil.get('in_air_cache')[pos+surface] = nil
	end
end

core.blockutil.in_air = function(pos)
	if core.storageutil.get('in_air_cache')[pos] then
		return true
	end
	for _,surface in pairs(normal_ids) do
		if core.blockutil.get_block(pos+surface) then
			return
		end
	end
	local ray = core.util.raycast(core.blockutil.from_grid(pos),Vector3.new(0,-block_size,0),true,Enum.RaycastFilterType.Exclude,{
		core.entityutil.get_entities(),
		core.storageutil.get('hitboxes'),
		core.fx,
		core.junk,
		script
	})
	if ray then
		return
	end
	--if config.debug then
	--	--print('in air')
	--end
	core.storageutil.get('in_air_cache')[pos] = 1
	return true
end

core.blockutil.entity_block_conflict = function(model,pos)
	if core.entityutil.is_alive(model) then
		local hb = core.entityutil.get_hitbox(model)

		pos = core.blockutil.from_grid(pos)

		local param = OverlapParams.new()
		param.FilterType = Enum.RaycastFilterType.Include
		param.FilterDescendantsInstances = {hb.box,core.storageutil.get('hitboxes')}

		local parts = workspace:GetPartBoundsInBox(CFrame.new(pos),BlockVector3-hitbox_allowance,param)
		local t = #parts

		hb.destroy()

		return t > 0
	end
end

core.util.round = function(n)
	if typeof(n) == 'Vector3' then
		local function f(n)
			return math.floor(n+0.5)
		end
		return Vector3.new(f(n.X),f(n.Y),f(n.Z))
	end
	return math.floor(n+0.5)
end

core.blockutil.to_grid = function(pos)
	local newpos = core.util.round(Vector3.new(pos.X/block_size,pos.Y/block_size,pos.Z/block_size))
	return newpos
end

core.blockutil.from_grid = function(pos)
	local newpos = Vector3.new(pos.X*block_size,pos.Y*block_size,pos.Z*block_size) + y_offset
	return newpos
end

core.blockutil.can_place = function(pos,no_walls,full_check)
	if core.storageutil.get('place_process')[pos] then return end
	if core.blockutil.get_block(pos) then return end

	local param = OverlapParams.new()
	param.FilterDescendantsInstances = {
		--core.blockutil.get_blocks(),
		core.junk
	}

	local parts = workspace:GetPartBoundsInBox(CFrame.new(core.blockutil.from_grid(pos)),BlockVector3-hitbox_allowance,param)

	local list = {}

	for i,v in ipairs(parts) do
		local model = v.Parent
		local thum = model:FindFirstChildOfClass("Humanoid")
		if core.storageutil.get('hitboxes')[model] and v == core.storageutil.get('hitboxes')[model] then
			return nil,model
		end
		if no_walls then
			return
		end
		if (v.CanCollide or v.Name == 'HumanoidRootPart') and full_check then
			return
		end
		if thum and not table.find(list,model) then
			table.insert(list,model)
		end
	end

	for i,v in ipairs(list) do
		if core.blockutil.entity_block_conflict(v,pos) then
			return 
		end
	end

	return true
end

core.crystalutil.can_place = function(pos)
	if core.blockutil.can_place(pos) and core.blockutil.get_block(pos+Vector3.new(0,-1,0)) then
		return true
	end
end

core.util.create_texture = function(part,normal,id,zindex,brightness)
	local sg = part:FindFirstChild('block_texture_' .. normal.Name)
	local image = nil
	
	if not sg then
		sg = Instance.new("SurfaceGui")
		sg.Name = 'block_texture_' .. normal.Name
		sg.ResetOnSpawn = true
		sg.Face = normal
		sg.Parent = part
		sg.ZIndexBehavior = Enum.ZIndexBehavior.Global
	end
	
	if brightness then
		sg.Brightness = brightness
		sg.LightInfluence = 0
	else
		sg.LightInfluence = 1
	end
	
	sg.ZOffset = zindex or 0
	
	image = sg:FindFirstChildOfClass('ImageLabel')
	if not image then
		image = Instance.new("ImageLabel")
		image.Size = UDim2.new(1,0,1,0)
		image.BackgroundTransparency = 1
		image.ResampleMode = Enum.ResamplerMode.Pixelated
	end
	
	image.ZIndex = zindex or 1
	
	image.Parent = nil
	image.Image = 'rbxassetid://' .. id
	game:GetService("ContentProvider"):PreloadAsync({image})
	image.Parent = sg
end

local wool = "rbxassetid://3843797566"
local metal = "rbxassetid://935281533"
local sand = "rbxassetid://3843792584"
local gravel = "rbxassetid://3843794655"
local wood1 = "rbxassetid://3843789432"
local wood2 = "rbxassetid://3843786781"
local grass = "rbxassetid://3843796214"
local snow = "rbxassetid://3843791122"

local obsidian_template = nil

do
	local Part1 = Instance.new("Part")
	Part1.Name = "Obsidian"
	Part1.Transparency = 0
	Part1.Size = BlockVector3
	Part1.BottomSurface = Enum.SurfaceType.Smooth
	Part1.CanCollide = true
	Part1.Anchored = true
	Part1.Massless = true
	Part1.CastShadow = false
	Part1.Material = Enum.Material.Fabric
	Part1.TopSurface = Enum.SurfaceType.Smooth
	
	for _,normal in pairs(Enum.NormalId:GetEnumItems()) do
		core.util.create_texture(Part1,normal,286047170)
	end
	
	obsidian_template = Part1
end

core.util.play = function(s)
	coroutine.wrap(function()
		game:GetService("ContentProvider"):PreloadAsync({s})
		if not s.Loaded then
			repeat task.wait() until s.Loaded
		end
		s:Play()
	end)()
end

core.blockutil.place = function(pos)
	if core.entityutil.is_alive(get_char().model) then else return end
	core.blockutil.reset_air(pos,true)
	if core.blockutil.can_place(pos) and not core.blockutil.in_air(pos) then
		core.blockutil.reset_air(pos)
		core.blockutil.process_block(pos,true)

		local obby = obsidian_template:Clone()
		obby.Position = core.blockutil.from_grid(pos)
		obby.Parent = script

		local placesound = Instance.new("Sound")
		placesound.SoundId = metal
		placesound.Volume = 1.5
		placesound.Parent = obby
		core.util.play(placesound)
		game:GetService("Debris"):AddItem(placesound,2)

		core.blockutil.set_block(pos,obby)
		core.blockutil.process_block(pos,false)

		return true
	end
end

core.crystalutil.update_entities = function()
	local ds = os.clock()-ls_anim

	for entity,_type in pairs(core.storageutil.get('entities')) do
		if core.entityutil.in_world(entity) then else
			core.entityutil.remove_entity(entity)
			continue
		end

		if _type == 'end crystal' and local_entities[entity] then
			local p1,p2,p3,main = entity.p1,entity.p2,entity.p3,entity.main

			if core.storageutil.get('anims')[entity] then else continue end

			local f = core.storageutil.get('anims')[entity].innerRotation
			local f1 = math.sin(f * 0.2)/2+0.5
			f1 = f1^2+f1
			local _p3 = f*3/60
			local _p4 = f1*0.4*block_size

			-- major replication lag, move this to client later
			main.CFrame = core.storageutil.get('anims')[entity].origin*CFrame.new(0,_p4,0)
			local rot = CFrame.Angles(0.7071,0,0.7071)*CFrame.Angles(0,1*_p3,0)
			p1.Weld.C1 = rot
			rot = rot*CFrame.Angles(0,1*_p3,0)*CFrame.Angles(0.7071*60,0,0.7071*60)
			p2.Weld.C1 = rot
			rot = rot*CFrame.Angles(0,1*_p3,0)*CFrame.Angles(0.7071*60,0,0.7071*60)
			p3.Weld.C1 = rot
			core.storageutil.get('anims')[entity].innerRotation += ds/(1/20)

			--[[
			main.CFrame = core.storageutil.get('anims')[entity].origin*CFrame.new(0,block_size/2*math.cos(core.storageutil.get('anims')[entity].time*5),0)
			core.storageutil.get('anims')[entity].time += ds

			p1.Weld.C1 = p1.Weld.C1*CFrame.Angles(0,math.rad(60*ds),0)
			p2.Weld.C1 = p2.Weld.C1*CFrame.Angles(math.rad(0*ds),0,math.rad(80*ds))
			p3.Weld.C1 = p3.Weld.C1*CFrame.Angles(math.rad(100*ds),0,math.rad(0*ds))
			]]
		end
	end
end

local crystal_template = nil

do
	local Model0 = Instance.new("Model")
	local Part1 = Instance.new("Part")
	local Part4 = Instance.new("Part")
	local Part6 = Instance.new("Part")
	local Part69 = Instance.new("Part")
	Model0.Name = "End Crystal"
	Part1.Name = "p1"
	Part1.Parent = Model0
	Part1.Orientation = Vector3.new(0, 45, 0)
	Part1.Rotation = Vector3.new(0, 45, 0)
	Part1.Color = Color3.new(0.666667, 0, 0.666667)
	Part1.Material = Enum.Material.Fabric
	Part1.Transparency = 1
	Part1.Size = BlockVector3*1
	Part1.Anchored = true
	Part1.BottomSurface = Enum.SurfaceType.Smooth
	Part1.BrickColor = BrickColor.new("Magenta")
	Part1.CanCollide = false
	Part1.TopSurface = Enum.SurfaceType.Smooth
	Part1.brickColor = BrickColor.new("Magenta")
	Part69.Name = "main"
	Part69.Parent = Model0
	Part69.Orientation = Vector3.new(0, 45, 0)
	Part69.Rotation = Vector3.new(0, 45, 0)
	Part69.Material = Enum.Material.SmoothPlastic
	Part69.Color = Color3.new(0, 0, 0)
	Part69.Transparency = 1
	Part69.Size = Vector3.new(0.05, 0.5, 0.05)
	Part69.Anchored = true
	Part69.CanCollide = false
	Part4.Name = "p2"
	Part4.Parent = Model0
	Part4.Orientation = Vector3.new(45, 0, 0)
	Part4.Rotation = Vector3.new(45, 0, 0)
	Part4.Color = Color3.new(0.666667, 0, 0.666667)
	Part4.Material = Enum.Material.Fabric
	Part4.Transparency = 1
	Part4.Size = BlockVector3*0.75
	Part4.Anchored = true
	Part4.BottomSurface = Enum.SurfaceType.Smooth
	Part4.BrickColor = BrickColor.new("Magenta")
	Part4.CanCollide = false
	Part4.TopSurface = Enum.SurfaceType.Smooth
	Part4.brickColor = BrickColor.new("Magenta")
	Part6.Name = "p3"
	Part6.Parent = Model0
	Part6.Orientation = Vector3.new(45, 0, 0)
	Part6.Rotation = Vector3.new(45, 0, 0)
	Part6.Color = Color3.new(0.666667, 0, 0.666667)
	Part6.Size = BlockVector3*0.75
	Part6.Transparency = 1
	Part6.Anchored = true
	Part6.BottomSurface = Enum.SurfaceType.Smooth
	Part6.BrickColor = BrickColor.new("Magenta")
	Part6.CanCollide = false
	Part6.Material = Enum.Material.Fabric
	Part6.TopSurface = Enum.SurfaceType.Smooth
	Part6.brickColor = BrickColor.new("Magenta")
	
	if not config.lfx then
		for _,normal in pairs(Enum.NormalId:GetEnumItems()) do
			core.util.create_texture(Part6,normal,4671609130,1)
		end
		for _,normal in pairs(Enum.NormalId:GetEnumItems()) do
			core.util.create_texture(Part4,normal,4671588152,2)
		end
		for _,normal in pairs(Enum.NormalId:GetEnumItems()) do
			core.util.create_texture(Part1,normal,4671588152,3)
		end
	else
		Part1.Transparency = 0.7
		Part6.Transparency = 0
		Part4.Transparency = 0.7
	end
	
	crystal_template = Model0
end

local last_crystal_instant = nil

core.crystalutil.place = function(pos)
	if core.entityutil.is_alive(get_char().model) then else return end
	core.blockutil.reset_air(pos,true)
	
	if core.crystalutil.can_place(pos) then -- 1.13+ placement
		if last_crystal_instant then
			core.crystalutil.explode(last_crystal_instant)
		end
		
		core.blockutil.reset_air(pos)
		core.blockutil.process_block(pos,true)
		
		local model = crystal_template:Clone()
		model.Name = 'end crystal'
		local p1 = model.p1
		local p2 = model.p2
		local p3 = model.p3
		local main = model.main

		local chum = Instance.new("Humanoid")
		chum.MaxHealth = 1 -- hard life..
		chum.Health = 1
		chum.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOff
		chum.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
		chum.Parent = model
		chum.HealthChanged:Connect(function()
			if core.storageutil.get('entities')[model] then else return end
			core.crystalutil.explode(model)
		end)

		do
			for i,v in ipairs(model:GetChildren()) do
				if v.Name ~= 'main' and v:IsA("BasePart") then
					local w = Instance.new("Weld")
					w.Part0 = main
					w.Part1 = v
					w.Parent = v

					v.Anchored = false
					v.Massless = true
					v.CastShadow = false
				end
			end

			main.Position = core.blockutil.from_grid(pos)+Vector3.new(0,block_size*0.1,0)
			main.Anchored = true

			core.storageutil.get('anims')[model] = {
				origin = main.CFrame,
				--time = os.clock(),
				innerRotation = math.random(10000),
			}

			--[[
			p1.Weld.C1 = CFrame.Angles(math.rad(45),0,math.rad(45))
			p2.Weld.C1 = CFrame.Angles(math.rad(45),math.rad(45),math.rad(45))
			p3.Weld.C1 = CFrame.Angles(0,math.rad(45),math.rad(45))
			]]
		end

		model.PrimaryPart = main
		main.Position = core.blockutil.from_grid(pos)

		local hb = core.entityutil.get_hitbox(model,false,BlockVector3*2-hitbox_allowance,Vector3.new(0,block_size/2,0),main.Position,true,model) -- *1.95 for block conflict

		local click = Instance.new("ClickDetector")
		click.MaxActivationDistance = 6*block_size
		click.Parent = hb.box

		click.MouseClick:Connect(function()
			if core.storageutil.get('entities')[model] then else return end
			click:Destroy()
			core.crystalutil.explode(model)
		end)

		core.entityutil.add_entity(model,'end crystal')
		
		model.Parent = core.fx

		core.crystalutil.update_entities()

		core.blockutil.process_block(pos,false)

		return model
	end
end

core.entityutil.set_hurt_sound = function(model)
	local root = model:FindFirstChild("HumanoidRootPart") or model:FindFirstChild("Torso") or model.PrimaryPart
	
	if root then
		if core.util.can_sound('hurt') then
			core.util.sound_debounce('hurt',1/10)
			local hs = Instance.new("Sound")
			hs.SoundId = 'rbxassetid://14424561569'
			hs.Volume = 0.5
			--hs.PlaybackSpeed = math.random(95,105)/100
			hs.Parent = root
			core.util.play(hs)
		end
	end
end

core.entityutil.set_hurt_ind = function(model)
	if model then else return end
	
	model:SetAttribute('hurt_st',os.clock())
	local hc = model:GetAttribute('hurt_st')
	
	for i,v in ipairs(model:GetChildren()) do
		if v:IsA("BasePart") and v.Name ~= 'HumanoidRootPart' then else
			continue
		end
		
		local hurt = v:FindFirstChild('hurt_highlight')
		
		if not hurt then
			hurt = Instance.new('Highlight')
			hurt.Name = 'hurt_highlight'
			hurt.DepthMode = Enum.HighlightDepthMode.Occluded
			hurt.FillTransparency = 0.5
			hurt.FillColor = Color3.new(1,0,0)
			hurt.OutlineTransparency = 1
			hurt.Enabled = false
			hurt.Adornee = v
			hurt.Parent = v
		end
		
		hurt.Enabled = true
		
		task.delay(0.5,function()
			if hc == model:GetAttribute('hurt_st') then else
				return
			end
			
			hurt.Enabled = false
		end)
	end
end

core.entityutil.set_immunity = function(thum,damage)
	core.storageutil.get('immunities')[thum] = {
		time = os.clock(),
		damage = damage
	}
end

core.entityutil.is_immune = function(thum)
	local immunity = core.storageutil.get('immunities')[thum]
	if immunity and 0.5 >= os.clock()-immunity.time then
		return true
	end
end

core.entityutil.immune_diff = function(thum,damage)
	local immunity = core.storageutil.get('immunities')[thum]
	if immunity then
		--if damage >= thum.Health then
		--	return damage
		--end
		local diff = damage-immunity.damage
		
		if diff > 0 then
			return diff
		end
	end
end

core.entityutil.take_damage = function(thum,damage)
	if core.entityutil.is_immune(thum) then
		local diff = core.entityutil.immune_diff(thum,damage)
		if diff then
			if (thum.Health - diff) > 0 then
				core.entityutil.set_hurt_sound(thum.Parent)
			end
			thum:TakeDamage(diff)
			
			if thum.Health >= 100000 and thum.Parent and thum.Parent.Name ~= 'Dummy' then
				thum.Health = 0
			end
		end
	else
		core.entityutil.set_hurt_sound(thum.Parent)
		core.entityutil.set_hurt_ind(thum.Parent)
		core.entityutil.set_immunity(thum,damage)
		thum:TakeDamage(damage)

		if thum.Health >= 100000 and thum.Parent and thum.Parent.Name ~= 'Dummy' then
			thum.Health = 0
		end
	end
end

core.crystalutil.calc_damage = function(entity,pos,do_damage)
	if entity then else return end
	
	if not do_damage then
		pos = pos+Vector3.new(0,block_size/2,0)+Vector3.new(0,-block_size+0.01,0)

		--[[
		if config.debug then
			local exp_part = Instance.new("Part")
			exp_part.Anchored = true
			exp_part.CanCollide = false
			exp_part.CanQuery = false
			exp_part.Transparency = 0
			exp_part.Size = Vector3.new(.5,.5,.5)
			exp_part.Position = pos
			exp_part.Parent = core.junk
			game:GetService("Debris"):AddItem(exp_part,0.5)
		end
		]]
	end

	local power = 6
	local radius = power*2

	local troot = entity:FindFirstChild("HumanoidRootPart") or entity:FindFirstChild("Torso") or entity.PrimaryPart
	
	if not troot then return end
	
	local tpos = troot.Position
	local feet_pos = tpos+Vector3.new(0,-3+0.01,0)
	local dist = (pos-feet_pos).Magnitude/block_size
	dist = core.util.round(dist)

	if radius >= dist then
		local exposure = core.entityutil.ray_entity(pos,entity,{},do_damage)
		if exposure then else return end
		local impact = (1-(dist/radius))*exposure
		local damage = 10.5*(impact*impact+impact)*power+1 -- easy = 3.5, normal = 7, hard = 10.5

		damage = damage*5 -- mc to roblox hp conversion
		--damage = core.util.round(damage)
		-- 125 damage against a block in mc (this does 127)

		if do_damage then
			--print(entity,math.floor(damage),exposure,dist)
			local thum = entity:FindFirstChildOfClass("Humanoid")
			if thum then
				core.entityutil.take_damage(thum,damage)
			end
		else
			return damage
		end
	end
end

core.util.sound_debounce = function(id,t,s)
	core.storageutil.get('sound_debounces')[id] = {
		st = os.clock(),
		t = t,
		sound = s,
	}
end
core.util.can_sound = function(id)
	if core.storageutil.get('sound_debounces')[id] then
		if core.storageutil.get('sound_debounces')[id].t >= os.clock()-core.storageutil.get('sound_debounces')[id].st then
			return
		end
	end
	return true
end

local explosions = {}

do
	local ids = {17495858639,17495859492,17495861983,17495862769}
	
	for i,v in pairs(ids) do
		local s = Instance.new("Sound")
		s.SoundId = "rbxassetid://" .. v
		s.Volume = 1
		--s.RollOffMaxDistance = 16*block_size*2
		game:GetService("ContentProvider"):PreloadAsync({s})
		table.insert(explosions,s)
	end
end

core.crystalutil.explode = function(model,chain,instant)
	if model and core.entityutil.get_type(model) == 'end crystal' then else
		return
	end

	local hb = core.entityutil.get_hitbox(model)
	local center_pos = hb.box.Position
	local pos = center_pos+Vector3.new(0,-block_size+0.01,0)

	core.entityutil.remove_entity(model)
	
	if last_crystal_instant then
		last_crystal_instant.Parent = nil
		last_crystal_instant:ClearAllChildren()
		last_crystal_instant:Destroy()
		last_crystal_instant = nil
	end
	
	if instant and config.instant_crystal_render then
		model.Parent = core.junk
		last_crystal_instant = model
		
		task.delay(1/10,function()
			if last_crystal_instant then else
				return
			end
			
			model.Parent = nil
			model:ClearAllChildren()
			model:Destroy()
		end)
	else
		model.Parent = nil
		model:ClearAllChildren()
		model:Destroy()
	end

	if chain then return end -- end crystals get destroyed in chains

	local exp_part

	if not chain then
		exp_part = Instance.new("Part")
		exp_part.Anchored = true
		exp_part.CanCollide = false
		exp_part.CanQuery = false
		exp_part.Massless = true
		exp_part.CastShadow = false
		exp_part.Transparency = 1
		exp_part.Size = Vector3.new(1,1,1)
		exp_part.Position = pos
		exp_part.Parent = core.junk

		if config.debug then
			exp_part.Transparency = 0
		end
		
		if not config.no_particles then
			local ParticleEmitter1 = Instance.new("ParticleEmitter")
			ParticleEmitter1.Parent = exp_part
			ParticleEmitter1.Speed = NumberRange.new(25,25)
			ParticleEmitter1.Color = ColorSequence.new(Color3.new(1,1,1),Color3.new(1,1,1))
			ParticleEmitter1.Enabled = false
			ParticleEmitter1.LightEmission = 0.80000001192093
			ParticleEmitter1.Texture = "rbxassetid://3607612871"
			ParticleEmitter1.Size = NumberSequence.new(0.5,1)
			ParticleEmitter1.Lifetime = NumberRange.new(0.5)
			ParticleEmitter1.LockedToPart = true
			ParticleEmitter1.Rate = 10000
			ParticleEmitter1.RotSpeed = NumberRange.new(270, 270)
			ParticleEmitter1.SpreadAngle = Vector2.new(-90, -90)
			ParticleEmitter1.VelocitySpread = -90
			ParticleEmitter1.Shape = Enum.ParticleEmitterShape.Sphere
			ParticleEmitter1:Emit(100)
		end
		
		if core.util.can_sound('explosion') then
			core.util.sound_debounce('explosion',1/20)
			
			local s = explosions[math.random(#explosions)]:Clone()
			s.Parent = exp_part
			core.util.play(s)
		end
		game:GetService("Debris"):AddItem(exp_part,3.9)
	end

	for entity,_type in pairs(core.storageutil.get('entities')) do
		if core.entityutil.in_world(entity) then else
			core.entityutil.remove_entity(entity)
			continue
		end

		local power = 6
		local radius = power*2

		if _type == 'player' and core.entityutil.is_alive(entity) and (not entity:FindFirstChild("ForceField") or entity == get_char().model and config.attack_self) and not chain then
			core.crystalutil.calc_damage(entity,pos,true)
		elseif _type == 'end crystal' then
			local hb = core.entityutil.get_hitbox(entity,true)

			local dist = (center_pos-hb.box.Position).Magnitude/block_size
			--dist = core.util.round(dist)

			if radius >= dist then
				core.crystalutil.explode(entity,true)
			end
		end
	end
	
	if not chain then
		return true
	end
end

pcall(function()
	game:GetService("PhysicsService"):CreateCollisionGroup('ray_entity')
	game:GetService("PhysicsService"):CreateCollisionGroup('ray_block')
	game:GetService("PhysicsService"):CollisionGroupSetCollidable('ray_entity','ray_block',false)
end)

local asd = {}

local cached_placement

core.crystalutil.auto_place = function(data,instant)
	local cpos = data.crystal_pos

	local best_place
	if not core.blockutil.get_block(cpos-Vector3.new(0,1,0)) then
		best_place = cpos-Vector3.new(0,1,0)
	end

	if config.auto_crystal then else return end
	
	if data.entity_collision then
		core.crystalutil.explode(data.entity_collision)
		return true
	end

	if best_place and config.place_support_block then
		--print(best_self_damage,best_damage)
		core.blockutil.place(best_place)

		local ps = math.clamp(config.place_speed,0,10)
		if config.place_speed > 0 then
			task.wait(ps)
		end

		--local target_dmg = core.crystalutil.calc_damage(target,core.blockutil.from_grid(best_crystal))
		--local self_dmg = core.crystalutil.calc_damage(get_char().model,core.blockutil.from_grid(best_crystal))
		--warn(self_dmg,target_dmg)
	end

	if config.auto_crystal then else return end

	local crystal
	if config.place_crystal then
		crystal = core.crystalutil.place(cpos)
	end
	
	if crystal then else
		return
	end
	
	if config.crystal_speed > 0 and not instant then
		if config.fast_place then
			task.wait(1/20)
		else
			task.wait(config.crystal_speed)
		end
	else
		instant = true
	end

	core.crystalutil.explode(crystal,nil,instant)
	
	if crystal then
		return true
	end
end

local cache_timeout = 0
local post_death_cache_timeout = 0

core.crystalutil.auto_crystal = function() -- scans for placeable blocks and crystals
	if core.entityutil.is_alive(get_char().model) and get_char().hum.Health >= config.minimum_health then else return end

	local st = os.clock()

	local closest_dist = 1/0
	local best_damage = 0
	local best_self_damage = 1/0
	local target,target_root
	
	if config.check_immunity_self_damage and get_char().model:FindFirstChildOfClass("ForceField") then
		best_self_damage = 0
	end
	
	local best_crystals = {}
	
	local bypass = false

	for entity,_type in pairs(core.storageutil.get('entities')) do
		if core.entityutil.in_world(entity) then else
			core.entityutil.remove_entity(entity)
			continue
		end
		
		if _type == 'player' and (entity ~= get_char().model or config.attack_self) then
			if core.entityutil.is_alive(entity) then
			elseif (config.smart_target and cached_placement and cached_placement.target == entity) then
				bypass = true
			else
				continue
			end
			if not entity:FindFirstChildOfClass("ForceField") or (config.attack_self and entity == get_char().model) then else
				continue
			end
			
			local troot = entity:FindFirstChild("HumanoidRootPart") or entity:FindFirstChild("Torso") or entity.PrimaryPart
			local thum = entity:FindFirstChildOfClass("Humanoid")
			
			if troot and thum then
				local dist = (troot.Position-get_char().root.Position).Magnitude
				if closest_dist > dist and config.target_range >= dist/block_size then
					if config.check_immunity and core.entityutil.is_immune(thum) and not core.entityutil.immune_diff(thum,config.minimum_target_damage) then
						continue
					end
					closest_dist = dist
					target = entity
					target_root = target:FindFirstChild("HumanoidRootPart") or target:FindFirstChild("Torso") or target.PrimaryPart
				end
			end
		end
	end
	
	if target and target_root then else
		if cached_placement then
			if config.smart_target then
				cache_timeout += 1
				if cache_timeout > config.cache_limit then
					cache_timeout = 0
					cached_placement = nil
					return
				else
					bypass = true
					target = cached_placement.target
				end
			else
				cached_placement = nil
				return
			end
		else
			return
		end
	end
	
	local function fast_place()
		local cpos = cached_placement.crystal_pos
		local block_pos = cpos-Vector3.new(0,1,0)
		
		if core.util.in_range(block_pos) then else
			cached_placement = nil
			return
		end
		
		if core.entityutil.is_alive(target) then else
			if not config.smart_target or 0 > config.post_death_cache_limit then
				cached_placement = nil
				return
			else
				post_death_cache_timeout += 1
				if post_death_cache_timeout > config.post_death_cache_limit then
					post_death_cache_timeout = 0
					cached_placement = nil
					return nil,true
				else
					bypass = true
				end
			end
		end
		
		if cached_placement.target == target then
		elseif config.smart_target and cached_placement.target.Name == target.Name then
		elseif config.smart_target and cached_placement.player and game:GetService("Players"):GetPlayerFromCharacter(target) == cached_placement.player then
		else
			cached_placement = nil
			return
		end
		
		local dist = (core.blockutil.to_grid(cached_placement.self_pos)-core.blockutil.to_grid(get_char().root.Position)).Magnitude
		if 2 >= dist then else
			cached_placement = nil
			return
		end
		
		if target_root then
			local dist = (core.blockutil.to_grid(cached_placement.target_pos)-core.blockutil.to_grid(target_root.Position)).Magnitude
			if 2 >= dist then else
				cached_placement = nil
				return
			end
		elseif post_death_cache_timeout > 0 then
		else
			cached_placement = nil
			return
		end
		
		if not core.blockutil.get_block(block_pos) and not core.blockutil.can_place(block_pos) then
			cached_placement = nil
			return
		end
		
		local crystal_can_place,entity_collision = core.blockutil.can_place(cpos)
		
		if not crystal_can_place and core.entityutil.get_type(entity_collision) ~= 'end crystal' then
			cached_placement = nil
			return
		end
		
		local box
		if not core.blockutil.get_block(block_pos) then
			box = Instance.new("Part")
			box.CanCollide = false
			box.Anchored = true
			box.CanTouch = false
			box.CastShadow = false
			box.CanQuery = true
			box.Massless = true
			box.Transparency = 0
			box.Size = BlockVector3
			box.Position = core.blockutil.from_grid(block_pos)
			box.Parent = workspace
			core.temp_block = box
		end

		local self_dmg = 0
		if best_self_damage > 0 then
			self_dmg = core.crystalutil.calc_damage(get_char().model,core.blockutil.from_grid(cpos))
			if config.attack_self then
				self_dmg = 0
			end

			if self_dmg and config.max_self_damage >= self_dmg then else
				if box then
					box:Destroy()
				end
				cached_placement = nil
				return
			end
		end
		
		local target_dmg = 0
		
		if target_root then
			target_dmg = core.crystalutil.calc_damage(target,core.blockutil.from_grid(cpos))
		elseif post_death_cache_timeout > 0 then
			target_dmg = 1/0
		end
		
		if target_dmg and target_dmg >= config.minimum_target_damage and target_dmg/self_dmg >= config.damage_balance then
			if box then
				box:Destroy()
			end
			
			local data = cached_placement
			data.entity_collision = entity_collision
			
			local placed = core.crystalutil.auto_place(cached_placement, post_death_cache_timeout > 0)
			
			return placed, cache_timeout > 0
		else
			if box then
				box:Destroy()
			end
			cached_placement = nil
		end
	end
	
	if config.fast_place and cached_placement then
		local placed,cancel = fast_place()
		if placed then
			if core.entityutil.is_alive(target) then
				cache_timeout = 0
				post_death_cache_timeout = 0
			end
			return
		end
		if bypass or cancel then
			return
		end
	end
	
	if config.debug then
		if #asd > 0 then
			for i,v in ipairs(asd) do
				v:Destroy()
			end
		end
	end

	local numba = 0

	local pos = core.blockutil.to_grid(get_char().root.Position)
	local blocks = core.blockutil.get_sphere(pos,config.range)

	for _,block_pos: Vector3 in ipairs(blocks) do
		local cpos = block_pos+Vector3.new(0,1,0)

		if core.blockutil.in_air(block_pos) then
			continue
		end
		if not core.blockutil.get_block(block_pos) and not core.blockutil.can_place(block_pos) then -- cant place in places without obsidian
			continue
		end
		if core.blockutil.get_block(cpos) then
			continue
		end
		local crystal_can_place,entity_collision = core.blockutil.can_place(cpos)
		if not crystal_can_place and core.entityutil.get_type(entity_collision) ~= 'end crystal' then -- 1.13+ check
			continue
		end

		if config.target_mode == 'damage' then
			local box = Instance.new("Part")
			box.CanCollide = false
			box.Anchored = true
			box.CanTouch = false
			box.CastShadow = false
			box.CanQuery = true
			box.Massless = true
			box.Transparency = 1
			box.Size = BlockVector3
			box.Position = core.blockutil.from_grid(block_pos)
			box.Parent = workspace
			core.temp_block = box

			--game:GetService("PhysicsService"):SetPartCollisionGroup(box,'ray_block')
			--for i,v in ipairs(target:GetDescendants()) do
			--	if v:IsA("BasePart") then
			--		game:GetService("PhysicsService"):SetPartCollisionGroup(v,'ray_entity')
			--	end
			--end
			local self_dmg = 0
			if best_self_damage > 0 then
				self_dmg = core.crystalutil.calc_damage(get_char().model,core.blockutil.from_grid(cpos))
				if config.attack_self then
					self_dmg = 0
				end

				if self_dmg and config.max_self_damage >= self_dmg and best_self_damage >= self_dmg then else
					box:Destroy()
					continue
				end
			end

			local target_dmg = core.crystalutil.calc_damage(target,core.blockutil.from_grid(cpos))

			numba += 1

			if target_dmg and target_dmg >= best_damage and target_dmg >= config.minimum_target_damage and target_dmg/self_dmg >= config.damage_balance then
				best_damage = target_dmg
				best_self_damage = self_dmg
				
				table.insert(best_crystals,1,{
					crystal_pos = cpos,
					entity_collision = entity_collision,
				})
			end

			box:Destroy()

			if config.fast_calc and #best_crystals >= config.fast_calc_limit then
				break
			end
		end

		--if config.debug then
		--	local box = Instance.new("Part")
		--	box.CanCollide = false
		--	box.Anchored = true
		--	box.CanTouch = false
		--	box.CanQuery = true
		--	box.Massless = true
		--	box.Color = Color3.new(1,0,0)
		--	box.Transparency = 0.5
		--	box.Size = BlockVector3*1.1
		--	box.Position = core.blockutil.from_grid(block_pos)
		--	--core.blockutil.create_selection(box)
		--	box.Parent = core.junk
		--	game:GetService("Debris"):AddItem(box,2)
		--	--table.insert(asd,box)
		--end
	end

	for _,data in ipairs(best_crystals) do
		local placed = core.crystalutil.auto_place(data)
		if placed then
			if config.fast_place and target_root then
				cache_timeout = 0
				post_death_cache_timeout = 0
				cached_placement = data
				cached_placement.target = target
				cached_placement.player = game:GetService("Players"):GetPlayerFromCharacter(target)
				cached_placement.target_pos = target_root.Position
				cached_placement.self_pos = get_char().root.Position
			end

			blocks = nil
			--warn(os.clock()-st,numba,numba*3)
			break
		end
	end
	
	blocks = nil
	--warn(os.clock()-st,numba,numba*3)
end

core.entityutil.is_entity = function(target)
	if not target then return end
	if not target:IsA("Model") then
		target = target:FindFirstAncestorOfClass("Model")
	end
	if target and (target:FindFirstChildOfClass("Humanoid") or core.storageutil.get('hitboxes')[target]) then
		return true
	end
end

core.util.mouse_place = function(mode,pos,target,surface)
	if core.entityutil.is_alive(get_char().model) then else return end

	if target and pos then else return end
	if core.entityutil.is_entity(target) then return end

	local normal = nil --core.blockutil.to_normal(surface)
	
	if surface then
		if typeof(surface) == 'Vector3' then else
			return
		end
		for i,v in pairs(normal_ids) do
			if surface == v then
				normal = surface
				break
			end
		end
		if normal then else
			return
		end
	end

	pos = core.util.round(core.blockutil.to_grid(pos+y_offset)+normal/2+Vector3.new(0,1,0)/2) -- adjusted to surface pos

	if target and core.blockutil.is_block(target) then
		local tpos = core.blockutil.to_grid(target.Position)
		local spos = tpos+normal -- might grid fail lol
		local cpos = tpos+Vector3.new(0,1,0)

		if mode == 'end crystal' then
			local crystal = core.crystalutil.place(cpos)
			
			if crystal and config.attack_placed_crystals then
				core.crystalutil.explode(crystal,nil,true)
			end
		elseif mode == 'block' then
			core.blockutil.place(spos)
		end
	elseif mode == 'block' then
		core.blockutil.place(pos)
	end
end

core.blockutil.auto_surround = function()
	if core.entityutil.is_alive(get_char().model) then else return end
	if config.auto_surround then else return end

	local death_con,root_con
	
	local function center_char()
		local raw_root_pos = get_char().root.Position
		local root_pos = core.blockutil.to_grid(get_char().root.Position)
		local root_pos2 = core.blockutil.from_grid(root_pos)

		get_char().root.Position = Vector3.new(root_pos2.X,raw_root_pos.Y,root_pos2.Z)
	end
	
	local function stop()
		if config.auto_surround then else return end
		config.auto_surround = false
		get_char().root.Anchored = false
		print('auto_surround: ' .. tostring(config.auto_surround))

		death_con:Disconnect()
		root_con:Disconnect()
	end

	death_con = get_char().hum.Died:Connect(function()
		death_con:Disconnect()
		root_con:Disconnect()
		repeat
			task.wait()
		until get_char().hum.Health and get_char().hum.Health > 0
		core.blockutil.auto_surround()
	end)
	
	local y_level = get_char().root.Position.Y

	root_con = get_char().root.Changed:Connect(function()
		if get_char().hum.Jump then
			stop()
			return
		end
		if math.abs(y_level-get_char().root.Position.Y)*block_size >= block_size*0.9 then
			stop()
			return
		end
	end)

	if get_char().hum.Jump then
		stop()
		return
	end

	center_char()

	while core.entityutil.is_alive(get_char().model) do
		if config.auto_surround then else return end

		local root_pos = core.blockutil.to_grid(get_char().root.Position)

		local spos = {
			Vector3.new(1,0,0),
			Vector3.new(-1,0,0),
			Vector3.new(0,0,1),
			Vector3.new(0,0,-1),
		}

		for i = 1,#spos do
			local spos = spos[i]

			if not core.blockutil.get_block(root_pos+spos) then
				get_char().root.Anchored = true
				if not core.blockutil.can_place(root_pos+spos,false,true) then
					center_char()
					root_pos = core.blockutil.to_grid(get_char().root.Position)
				end
				if get_char().hum.Jump then
					stop()
					return
				end
				if math.abs(y_level-get_char().root.Position.Y) >= 0.1 then
					stop()
					return
				end
				if config.auto_surround then else
					stop()
					return
				end
				local placed = core.blockutil.place(root_pos+spos)
				if not placed then
					center_char()
					root_pos = core.blockutil.to_grid(get_char().root.Position)
				end
			end
		end
		get_char().root.Anchored = false

		task.wait(config.surround_speed+0.5)
	end
end

local packets = {
	['place'] = function(data)
		core.util.mouse_place('block',data.pos,data.target,data.surface)
	end,
	['place_crystal'] = function(data)
		core.util.mouse_place('end crystal',data.pos,data.target,data.surface)
	end,
	['mine'] = function(data)
		core.blockutil.mine_block(data.target)
	end,
	['auto_crystal'] = function()
		config.auto_crystal = not config.auto_crystal
		print('auto_crystal: ' .. tostring(config.auto_crystal))

		while config.auto_crystal do
			core.crystalutil.auto_crystal()

			if config.fast_place and cached_placement then
				task.wait()
			else
				task.wait(config.check_speed)
			end
		end
	end,
	['auto_surround'] = function()
		config.auto_surround = not config.auto_surround
		print('auto_surround: ' .. tostring(config.auto_surround))

		core.blockutil.auto_surround()
	end,
	['attack_placed_crystals'] = function()
		config.attack_placed_crystals = not config.attack_placed_crystals
		print('attack_placed_crystals: ' .. tostring(config.attack_placed_crystals))
	end
}

local function run_client()
	local remote = Instance.new("RemoteEvent")
	remote.Parent = core.plr:FindFirstChildOfClass("PlayerGui")

	remote.OnServerEvent:Connect(function(lplr,packet,...)
		if lplr == core.plr then else return end
		local func = packets[packet]
		if func then
			func(...)
		end
	end)

	NLS([[
local remote = script.Parent
local uis = game:GetService("UserInputService")
local mouse = game:GetService("Players").LocalPlayer:GetMouse()
local lplr = game.Players.LocalPlayer
local char = lplr.Character
local head = char:WaitForChild("Head")

local place_hold = false
local mine_hold = false
local crystal_hold = false

local function is_fps()
	return head.LocalTransparencyModifier == 1
end

local function raycast()
	local param = RaycastParams.new()
	param.FilterDescendantsInstances = {char}
	param.IgnoreWater = true

	local origin = workspace.CurrentCamera.CFrame.Position
	local dir = (mouse.Hit.Position-origin).Unit*1000
	local dist = dir.Magnitude

	local ray = workspace:Raycast(origin,dir,param)

	local t = {
		pos = ray and ray.Position or mouse.Hit.Position,
		target = ray and ray.Instance,
		surface = ray and ray.Normal
	}
	
	return t
end

uis.InputBegan:Connect(function(input,press)
	if press then return end
	
	if input.UserInputType == Enum.UserInputType.Keyboard then
		local key = input.KeyCode
		
		if key == Enum.KeyCode.E then
			--mine_hold = false
			place_hold = false
			crystal_hold = true
			
			repeat
				remote:FireServer('place_crystal',raycast())
				task.wait(1/20)
			until not crystal_hold
		elseif key == Enum.KeyCode.F then
			--crystal_hold = false
			place_hold = false
			mine_hold = true

			repeat
				remote:FireServer('mine',raycast())
				task.wait(1/5)
			until not mine_hold
		elseif key == Enum.KeyCode.C then
			remote:FireServer('auto_crystal')
		elseif key == Enum.KeyCode.R then
			remote:FireServer('auto_surround')
		elseif key == Enum.KeyCode.T then
			remote:FireServer('attack_placed_crystals')
		end
	elseif input.UserInputType == Enum.UserInputType.MouseButton2 and is_fps() then
		mine_hold = false
		crystal_hold = false
		place_hold = true

		repeat
			remote:FireServer('place',raycast())
			task.wait(1/10)
		until not place_hold
	end
end)

uis.InputEnded:Connect(function(input,press)
	if input.UserInputType == Enum.UserInputType.Keyboard then
		local key = input.KeyCode

		if key == Enum.KeyCode.E then
			crystal_hold = false
		elseif key == Enum.KeyCode.F then
			mine_hold = false
		end
	elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
		place_hold = false
	end
end)
]],remote)
end

run_client()

core.plr.CharacterAdded:Connect(function()
	task.wait(0.2)

	run_client()
end)

local last_place = os.clock()

game:GetService("RunService").Stepped:Connect(function()
	core.crystalutil.update_entities()

	ls_anim = os.clock()
end)

local pings = {}
local ping = core.storageutil.get('ping',true)
if not ping and game:GetService("RunService"):IsServer() then
	ping = core.storageutil.set('ping',Instance.new("BindableEvent"))
end
ping.Event:Connect(function(id)
	if id ~= core.plr.UserId then
		pings[id] = os.clock()
	end
end)
coroutine.wrap(function()
	while true do
		ping:Fire(core.plr.UserId)
		for id,st in pairs(pings) do
			if os.clock()-st >= 3 then
				pings[id] = nil
				core.util.clean_up()
				break
			end
		end
		wait(0.5+math.random()*0.5)
	end
end)()

coroutine.wrap(function()
	while true do
		core.util.clean_up()
		wait(60)
	end
end)()

warn([[
-- Made by LazaroValkyrie
-- Crystal PvP v1.3

Must be in First Person
(Auto Runs on spawn)

RMB - Place
E - Place Crystal
F - Mine Block/Crystal
C - Auto Crystal
R - Auto Surround
T - Attack Placed Crystal Setting
]])
INFO