GM.CraftingRange = 72



GM.Crafts = {

	{

		Name = "a big wooden crate",

		a = {"*physics*", {"models/props_junk/wood_crate001a.mdl", "models/props_junk/wood_crate001a_damaged.mdl", "models/props_junk/wood_crate001a_damagedmax.mdl"}},

		b = {"*physics*", {"models/props_junk/wood_crate001a.mdl", "models/props_junk/wood_crate001a_damaged.mdl", "models/props_junk/wood_crate001a_damagedmax.mdl"}},

		Result = {"prop_physics", Model("models/props_junk/wood_crate002a.mdl")}

	},

	{

		Name = "a bust-on-a-stick",

		callback = function(enta, entb)

			return enta:IsPhysicsModel("models/props_combine/breenbust.mdl") and entb:IsWeaponType("weapon_zs_plank")

		end,

		Result = {"weapon_zs_bust"}

	},

	{

		Name = "an oil-filled barrel",

		a = {"*physics*", "models/props_junk/gascan001a.mdl"},

		b = {"*physics*", "models/props_c17/oildrum001.mdl"},

		Result = {"prop_physics", Model("models/props_c17/oildrum001_explosive.mdl")}

	},

	{

		Name = "a sawhack",

		callback = function(enta, entb)

			return enta:IsPhysicsModel("models/props_junk/sawblade001a.mdl") and entb:IsWeaponType("weapon_zs_axe")

		end,

		Result = {"weapon_zs_sawhack"}

	},

	{

		Name = "a mega masher",

		callback = function(enta, entb)

			return enta:IsPhysicsModel("models/props_c17/oildrum001_explosive.mdl") and entb:IsWeaponType("weapon_zs_sledgehammer")

		end,

		Result = {"weapon_zs_megamasher"}

	},

	{

		Name = "an electrohammer",

		callback = function(enta, entb)

			return enta:IsPhysicsModel("models/items/car_battery01.mdl") and entb:IsWeaponType("weapon_zs_hammer")

		end,

		Result = {"weapon_zs_electrohammer"}

	},

	{

		Name = "a 'Waraxe' handgun",

		callback = function(enta, entb)

			return enta:IsWeaponType("weapon_zs_battleaxe") and entb:IsWeaponType("weapon_zs_battleaxe")

		end,

		Result = {"weapon_zs_waraxe"}

	},

	{

		Name = "a modified manhack",

		callback = function(enta, entb)

			return enta:IsPhysicsModel("models/props_junk/sawblade001a.mdl") and entb:IsWeaponType("weapon_zs_manhack")

		end,

		Result = {"weapon_zs_manhack_saw"}

	}

}



function GM:GetCraftingRecipe(enta, entb)

	if not enta:IsValid() or not entb:IsValid() or enta == entb then return end



	for _, recipe in pairs(self.Crafts) do

		if self:CraftingRecipeMatches(enta, entb, recipe) then return recipe end

	end

end



function GM:GetCraftTarget(enta, entb)

	return enta:OBBMaxs():Distance(enta:OBBMins()) > entb:OBBMaxs():Distance(entb:OBBMins()) and enta or entb

end



function GM:CraftingRecipeMatches(enta, entb, recipe, checkedswap)

	local entaclass = enta:GetClass()

	local entbclass = entb:GetClass()

	local entaisphysics = string.sub(entaclass, 1, 12) == "prop_physics"

	local entbisphysics = string.sub(entbclass, 1, 12) == "prop_physics"



	if recipe.callback and recipe.callback(enta, entb) then

		return true

	end



	if recipe.a and recipe.b and (recipe.a[1] == entaclass or entaisphysics and recipe.a[1] == "*physics*") and (recipe.b[1] == entbclass or entaisphysics and recipe.a[1] == "*physics*") then

		local mdla = string.lower(enta:GetModel())

		local mdlb = string.lower(entb:GetModel())

		if (recipe.a[2] == nil or type(recipe.a[2]) == "table" and table.HasValue(recipe.a[2], mdla) or mdla == recipe.a[2]) and (recipe.b[2] == nil or type(recipe.b[2]) == "table" and table.HasValue(recipe.b[2], mdlb) or mdlb == recipe.b[2]) then

			return true

		end

	end



	if not checkedswap then

		return self:CraftingRecipeMatches(entb, enta, recipe, true)

	end

end



function GM:CanCraft(pl, enta, entb)

	if self:GetCraftingRecipe(enta, entb) == nil or not pl:IsValid() or not pl:Alive() or pl:Team() ~= TEAM_HUMAN then return false end



	if enta:IsBarricadeProp() or entb:IsBarricadeProp() or enta:GetName() ~= "" or entb:GetName() ~= "" then return false end



	local nearestb = entb:NearestPoint(enta:LocalToWorld(enta:OBBCenter()))

	local nearesta = enta:NearestPoint(entb:LocalToWorld(entb:OBBCenter()))

	if nearesta:Distance(nearestb) > self.CraftingRange or not TrueVisibleFilters(nearesta, nearestb, pl, enta, entb) then return false end



	local eyepos = pl:EyePos()

	if enta:NearestPoint(eyepos):Distance(eyepos) > self.CraftingRange or entb:NearestPoint(eyepos):Distance(eyepos) > self.CraftingRange then return false end



	if not TrueVisibleFilters(nearesta, eyepos, pl, enta, entb) or not TrueVisibleFilters(nearestb, eyepos, pl, enta, entb) then return false end



	return true

end



local meta = FindMetaTable("Entity")

if not meta then return end



function meta:IsPhysicsModel(mdl)

	return string.sub(self:GetClass(), 1, 12) == "prop_physics" and (not mdl or string.lower(self:GetModel()) == string.lower(mdl))

end



function meta:IsWeaponType(class)

	return self:GetClass() == "prop_weapon" and self:GetWeaponType() == class

end



if true then return end



function GM:GetCraftingRecipe(enta, entb)

	if not enta:IsValid() or not entb:IsValid() or enta == entb then return end



	for _, recipe in pairs(self.Crafts) do

		if self:CraftingRecipeMatches(enta, entb, recipe) then return recipe end

	end

end



function GM:GetCraftTarget(enta, entb)

	return enta:OBBMaxs():Distance(enta:OBBMins()) > entb:OBBMaxs():Distance(entb:OBBMins()) and enta or entb

end



function GM:CraftingRecipeMatches(enta, entb, recipe, checkedswap)

	local entaclass = enta:GetClass()

	local entbclass = entb:GetClass()

	local entaisphysics = string.sub(entaclass, 1, 12) == "prop_physics"

	local entbisphysics = string.sub(entbclass, 1, 12) == "prop_physics"



	if recipe.callback and recipe.callback(enta, entb) then

		return true

	end



	if recipe.a and recipe.b and (recipe.a[1] == entaclass or entaisphysics and recipe.a[1] == "*physics*") and (recipe.b[1] == entbclass or entaisphysics and recipe.a[1] == "*physics*") then

		local mdla = string.lower(enta:GetModel())

		local mdlb = string.lower(entb:GetModel())

		if (recipe.a[2] == nil or type(recipe.a[2]) == "table" and table.HasValue(recipe.a[2], mdla) or mdla == recipe.a[2]) and (recipe.b[2] == nil or type(recipe.b[2]) == "table" and table.HasValue(recipe.b[2], mdlb) or mdlb == recipe.b[2]) then

			return true

		end

	end



	if not checkedswap then

		return self:CraftingRecipeMatches(entb, enta, recipe, true)

	end

end



function GM:GetAllValidCraftingRecipes(pl)

	local craftable = {}



	for _, craft in pairs(self.Crafts) do

		if self:CanCraft(pl, craft) then

			table.insert(craftable, craft)

		end

	end



	return craftable

end



-- Preliminary tests for all entity types.

-- Classes are enforced by the recipes so if someone makes a recipe inolving players or worldspawn then oh well.

function GM:IsValidCraftingEntity(ent, pl)

	return not ent:IsWeapon()

	and not (ent:IsPhysicsModel() and ent:GetName() ~= "")

	and not (ent:IsWeapon() and ent:GetOwner() ~= pl)

end



-- This checks everything and is what determines if a recipe displays to the player in their menu.

function GM:CanCraft(pl, craft)

	if not pl:IsValid() or not pl:Alive() or pl:Team() ~= TEAM_HUMAN then return false end



	local plpos = pl:EyePos()

	local entities = ents.FindInSphere(plpos, self.CraftingRange)



	if craft.CanCraft and not craft:CanCraft(pl, entities, plpos) then return false end



	local ingredients = {}



	for _, ent in pairs(entities) do

		if craft.CanUseEntity and not craft:CanUseEntity(ent, pl) then continue end

	end



	-- All ingredients accounted for?

	--for _, ingredient



	local nearestb = entb:NearestPoint(enta:LocalToWorld(enta:OBBCenter()))

	local nearesta = enta:NearestPoint(entb:LocalToWorld(entb:OBBCenter()))

	if nearesta:Distance(nearestb) > self.CraftingRange or not TrueVisibleFilters(nearesta, nearestb, pl, enta, entb) then return false end



	local eyepos = pl:EyePos()

	if enta:NearestPoint(eyepos):Distance(eyepos) > self.CraftingRange or entb:NearestPoint(eyepos):Distance(eyepos) > self.CraftingRange then return false end



	if not TrueVisibleFilters(nearesta, eyepos, pl, enta, entb) or not TrueVisibleFilters(nearestb, eyepos, pl, enta, entb) then return false end



	return true

end



local meta = FindMetaTable("Entity")

if not meta then return end



function meta:IsPhysicsModel(mdl)

	return string.sub(self:GetClass(), 1, 12) == "prop_physics" and (not mdl or string.lower(self:GetModel()) == string.lower(mdl))

end

