--[[

]]

---------------------------------------- Variables ----------------------------------------

local lastX = -1;
local lastY = -1;

local labEqupmentSquares = {}


-- Vaciine effectiveness																							Time: 1 = 1h, 24 = 1d										(количество тиков, сколько раз происходит рецессия)
-- 										Сброс заражения		Не действует																	Общее количество альбумина,			Замедление развития вируса 
local vaccineEffect = {					--    до: 			после:			Шанс излечиться:	Антитела:		Время и защита от вируса:	Min + Rand Дельты:					на ранней стадии:
	CmpSyringeWithPlainVaccine = 			{ Min = 0.05,	Max = 0.87,		CureChance = 0,		IsSerum = 0,	Time = 0,  Strength = 0,	AlbuminMin = 5, AlbuminDelta = 4,	Recess = 12, },
	CmpSyringeWithQualityVaccine = 			{ Min = 0.05,	Max = 0.87, 	CureChance = 0,		IsSerum = 1,	Time = 25, Strength = 100,	AlbuminMin = 9, AlbuminDelta = 5,	Recess = 18, },
	CmpSyringeWithCure = 					{ Min = -1,		Max = 0.87, 	CureChance = 105,	IsSerum = 2,	Time = 25, Strength = 100,	AlbuminMin = 0, AlbuminDelta = 0,	Recess = 0, },
	CmpSyringeWithSerum = 					{ Min = -1,		Max = 0.87, 	CureChance = 105,	IsSerum = 3,	Time = 49, Strength = 100,	AlbuminMin = 0, AlbuminDelta = 0,	Recess = 0, },

	CmpSyringeReusableWithPlainVaccine = 	{ Min = 0.05,	Max = 0.87,		CureChance = 0,		IsSerum = 0,	Time = 0,  Strength = 0,	AlbuminMin = 5, AlbuminDelta = 4,	Recess = 12, },
	CmpSyringeReusableWithQualityVaccine = 	{ Min = 0.05,	Max = 0.87, 	CureChance = 0,		IsSerum = 1,	Time = 25, Strength = 100,	AlbuminMin = 9, AlbuminDelta = 5,	Recess = 18, },
	CmpSyringeReusableWithCure = 			{ Min = -1,		Max = 0.87, 	CureChance = 105,	IsSerum = 2,	Time = 25, Strength = 100,	AlbuminMin = 0, AlbuminDelta = 0,	Recess = 0, },
	CmpSyringeReusableWithSerum = 			{ Min = -1,		Max = 0.87, 	CureChance = 105,	IsSerum = 3,	Time = 49, Strength = 100,	AlbuminMin = 0, AlbuminDelta = 0,	Recess = 0, },
}

-- Chlorine tablets per water container
local chlorineDose = {
	Default = 3,
	LabFlaskWater = 1,
	WaterMug = 1,
	WaterBowl = 1,
	BeerWaterFull = 2,
	WaterBottleFull = 2,
	WaterPopBottle = 2,
	MayonnaiseWaterFull = 2,
	RemouladeWaterFull = 2,
	WhiskeyWaterFull = 2,
	WineWaterFull = 2,
	WaterBleachBottle = 3,
	WaterSaucepan = 3,
	FullKettle = 3,
	WaterPot = 3,
	WateredCanFull = 5,
	BucketWaterFull = 5,
	WaterPaintbucket = 5,
}


---------------------------------------- Test ----------------------------------------

-- Count items of ItemType in cell
function LabRecipes_CountCellItems(itemType)
	local cell = getWorld():getCell();
	local n = 0;
	for y = cell:getMinY(), cell:getMaxY() do
		for x = cell:getMinX(), cell:getMaxX() do
			for z = cell:getMinZ(), cell:getMaxZ() do
				local square = cell:getGridSquare(x,y,z);
				if square then
					local items = square:getObjects();
					for i = 0, items:size()-1 do
						local cnt = items:get(i):getContainer();
						if cnt then
							n = n+cnt:getItemCountRecurse(itemType);
                        end
					end
				end
			end
		end
	end
	return n;
end

---------------------------------------- Common ----------------------------------------

-- Check if player position changed with accuracy of 1/100 of a square
-- Need this cause game spam OnTest events like hell
function LabRecipes_IsPlayerMoved(player)
	local newX = math.floor(player:getX()*100);
	local newY = math.floor(player:getY()*100);
	if newX == lastX and newY == lastY then
		return false;
	end
	lastX = newX;
	lastY = newY;
	return true;
end

-- Look if there is Equipment or Custom name object near Square
function LabRecipes_LookForNearEquipment(square, equipment, custom)
	local px = square:getX();
	local py = square:getY();
	local pz = square:getZ();
	for y = py-1, py+1 do
		for x = px-1, px+1 do
			local squareTest = getCell():getGridSquare(x, y, pz);
			if squareTest then
				for i = 0, squareTest:getObjects():size()-1 do
					local sp = squareTest:getObjects():get(i):getSprite():getProperties();
					if equipment and sp:Is("CustomItem") and sp:Val("CustomItem") == equipment then
						return squareTest;
					elseif custom and sp:Is("GroupName") and sp:Is("CustomName") and sp:Val("GroupName").." "..sp:Val("CustomName") == custom then
						return squareTest;
					end
				end
			end
		end
	end
	return nil;
end

-- Check for player movement and look for given equipment
function LabRecipes_IsNearLabEquip(equipment)
	local player = getPlayer();
	labEqupmentSquares.SkipCheck = not(LabRecipes_IsPlayerMoved(player)) and labEqupmentSquares.LastEquipment == equipment;
	if labEqupmentSquares.SkipCheck then
		return labEqupmentSquares[equipment];
	end
	labEqupmentSquares.LastEquipment = equipment;
	labEqupmentSquares[equipment] = LabRecipes_LookForNearEquipment(player:getCurrentSquare(), "LabItems."..equipment, nil);
	return labEqupmentSquares[equipment];
end

--- Count how many itemType drainable pieces in inventory
function LabRecipes_CountDrainablePieces(container, itemType)
	local items = container:getItems();
	local count = 0;
    if items then
		for i = 0, items:size()-1 do
			local item = items:get(i);
			if item then
				if item:getCategory() == "Container" then
					count = count+LabRecipes_CountDrainablePieces(item:getItemContainer(), itemType);
                elseif item:getType() == itemType then
					count = count+math.floor(item:getUsedDelta()/item:getUseDelta()+0.5);
				end
			end
		end
	end
	return count;
end


---------------------------------------- Injection ----------------------------------------

-- Return zombie virus infection rate within 0..1 interval
function LabRecipes_InfectionRate(player)
	local body = player:getBodyDamage();
    return (player:getHoursSurvived()-body:getInfectionTime())/body:getInfectionMortalityDuration();
end

-- Cure player
function LabRecipes_CurePlayer(body)
	body:setInfected(false);
	body:setInfectionLevel(0);
	body:setInfectionMortalityDuration(-1);
	body:setInfectionTime(-1);
	local parts = body:getBodyParts();
	for i = 0, parts:size()-1 do
		parts:get(i):SetInfected(false);
	end
end

-- Inject a vaccine with a variety of results
function LabRecipes_DoInjection(player, vaccine)
	local body = player:getBodyDamage();
	local pMod = player:getModData();
	pMod.AlbuminDoses = 0;
	pMod.VaccineRecess = 0;
	pMod.VaccineTime = 0;
	pMod.VaccineStrength = 0;
	local traitSusceptible = TraitFactory.getTrait("Susceptible");
	if body:isInfected() then	-- ЗАРАЖЁН
		if vaccine.CureChance > ZombRand(100) then
			LabRecipes_CurePlayer(body);
		else
			local rate = LabRecipes_InfectionRate(player);
			if rate <= vaccine.Max then
				body:setInfectionTime(player:getHoursSurvived()-vaccine.Min*body:getInfectionMortalityDuration());
				if vaccine.AlbuminMin > 0 then
					player:getModData().AlbuminDoses = vaccine.AlbuminMin+ZombRand(vaccine.AlbuminDelta);
				end
				
			end
			if rate <= 30 then
				player:getModData().VaccineRecess = vaccine.Recess;
			end
		end
		if vaccine.IsSerum == 3 then	
			if (player:HasTrait("zReAntibodies") == false) then
				local SerumRand = ZombRand(100);
				if (pMod.VaccineSerumDoses == nil) then
					pMod.VaccineSerumDoses = ZombRand(5,13);
				end
				if pMod.VaccineSerumDoses > SerumRand then
					player:getTraits():add("zReAntibodies");
				 else
					pMod.VaccineSerumDoses = pMod.VaccineSerumDoses + ZombRand(1,6);
				end
			end
		end
		if (traitSusceptible ~= nil) and (vaccine.IsSerum >= 2) then
			if (player:HasTrait("Susceptible") == true) then
				player:getTraits():remove("Susceptible");
			end
		end
	 else	-- НЕ ЗАРАЖЁН
		if (traitSusceptible ~= nil) and (vaccine.IsSerum >= 1) then
			if (player:HasTrait("Susceptible") == true) then
				player:getTraits():remove("Susceptible");
			end
		end
		pMod.VaccineTime = vaccine.Time;
		pMod.VaccineStrength = vaccine.Strength;
	end
end

---------------------------------------- OnTest ----------------------------------------

-- Checks if there is Microscope near by
function LabRecipes_IsNearMicroscope(sourceItem, result)
	return LabRecipes_IsNearLabEquip("LabMicroscope") ~= nil;
end

-- Checks if there is Chromatograph with Computer near by
function LabRecipes_IsNearChromatographWithComputer(sourceItem, result)
	local square = LabRecipes_IsNearLabEquip("LabChromatograph");
	if not(square) then
		labEqupmentSquares.DesktopComputer = nil
	elseif square and not(labEqupmentSquares.SkipCheck) then
		labEqupmentSquares.DesktopComputer = LabRecipes_LookForNearEquipment(square, nil, "Desktop Computer");
	end
	return labEqupmentSquares.DesktopComputer ~= nil;
end

-- Checks if there is Spectrometer near by
function LabRecipes_IsNearSpectrometer(sourceItem, result)
	return LabRecipes_IsNearLabEquip("LabSpectrometer") ~= nil;
end

-- Checks if there is ChemistrySet near by, and we are using clear water
function LabRecipes_IsNearChemistrySet(sourceItem, result)
	if sourceItem:isWaterSource() and sourceItem:isTaintedWater() then
		return false;
	end
	return LabRecipes_IsNearLabEquip("LabChemistrySet") ~= nil;
end

-- Checks if there is Centrifuge near by
function LabRecipes_IsNearCentrifuge(sourceItem, result)
	return LabRecipes_IsNearLabEquip("LabCentrifuge") ~= nil;
end

-- Checks if there is Muffle Furnace near by
function LabRecipes_IsNearMuffleFurnace(sourceItem, result)
	return LabRecipes_IsNearLabEquip("LabMuffleFurnace") ~= nil;
end

-- Checks if there is Workbench near by
function LabRecipes_IsNearWorkbench(sourceItem, result)
	return LabRecipes_IsNearLabEquip("LabWorkbench") ~= nil;
end

-- Checks if there is Easel near by
function LabRecipes_IsNearEasel(sourceItem, result)
	return LabRecipes_IsNearLabEquip("LabEasel") ~= nil;
end


---------------------------------------- OnCreate ----------------------------------------

function LabRecipes_CreateBloodTestResult(items, result, player)
	local inv = player:getInventory();
	if not (player:getBodyDamage():isInfected()) then
		inv:AddItem("LabItems.LabTestResultNegative");
	 else
		local testResult = inv:AddItem("LabItems.LabTestResultPositive");
		local infectionRate = LabRecipes_InfectionRate(player);
		testResult:setName(testResult:getName().." ("..math.floor(infectionRate*100).."%)");
		testResult:setCustomName(true);
	end
	for i = 0, items:size()-1 do
		if (items:get(i):getType() == "LabSyringe") then
			inv:AddItem("LabItems.LabSyringeUsed");
		elseif (items:get(i):getType() == "LabSyringeReusable") then
			inv:AddItem("LabItems.LabSyringeReusableUsed");
		end
	end
end

-- Embed infection rate into Syringe With Blood	
-- 500ml = 100%hp | 300ml = 60%hp | 25ml = 5%hp but x DeltaHP = ~7% = 1 Syringe
function LabRecipes_ProcessBloodForAnalysis(items, result, player)
	for i = 0, items:size()-1 do
		local item = items:get(i);
		if item:getType() == "LabSyringe" then
			local cmpSyringe = InventoryItemFactory.CreateItem("LabItems.CmpSyringeWithBlood");
			local iMod = cmpSyringe:getModData();
			iMod.IsInfected = player:getBodyDamage():isInfected();
			iMod.InfectionRate = LabRecipes_InfectionRate(player);
			iMod.IsAntibodied = player:HasTrait("zReAntibodies");
			player:getInventory():AddItem(cmpSyringe);
		elseif item:getType() == "LabSyringeReusable" then
			local cmpSyringe = InventoryItemFactory.CreateItem("LabItems.CmpSyringeReusableWithBlood");
			local iMod = cmpSyringe:getModData();
			iMod.IsInfected = player:getBodyDamage():isInfected();
			iMod.InfectionRate = LabRecipes_InfectionRate(player);
			iMod.IsAntibodied = player:HasTrait("zReAntibodies");
			player:getInventory():AddItem(cmpSyringe);
		end
		break;
	end
	local researchReduceHealth = SandboxVars.zReV2.ResearchReduceHealth;
	local researchMinHealthNotification = SandboxVars.zReV2.ResearchMinHealthNotification;
	player:getBodyDamage():ReduceGeneralHealth(researchReduceHealth);
	if player:getBodyDamage():getOverallBodyHealth() < researchMinHealthNotification then
		player:Say(getText("IGUI_zReVaccineLessHp"));
	end
end

-- Change test result item if result is positive
function LabRecipes_CreateBloodTestAnalysis(items, result, player)
	for i = 0, items:size()-1 do
		local item = items:get(i);
		if item:getType() == "CmpSyringeWithBlood" or item:getType() == "CmpSyringeReusableWithBlood" then
			if item:getType() == "CmpSyringeWithBlood" then
				player:getInventory():AddItem("LabItems.LabSyringeUsed");
			 else --item:getType() == "CmpSyringeReusableWithBlood"
				player:getInventory():AddItem("LabItems.LabSyringeReusableUsed");
			end
			local cmpTestTubeWithInfectedBlood = InventoryItemFactory.CreateItem("LabItems.CmpTestTubeWithInfectedBlood");
			local iMod = cmpTestTubeWithInfectedBlood:getModData();
			iMod.IsInfected = item:getModData().IsInfected;
			iMod.InfectionRate = item:getModData().InfectionRate;
			iMod.IsAntibodied = item:getModData().IsAntibodied;

			if not iMod.IsInfected then
				if not iMod.IsAntibodied then	-- не заражён, нет устойчивых антител
					iMod.BloodCellsChance = -20;
					cmpTestTubeWithInfectedBlood:setName(cmpTestTubeWithInfectedBlood:getName().." (V-, A-)");
				 else							-- не заражён, есть устойчивые антитела
					iMod.BloodCellsChance = 25;
					cmpTestTubeWithInfectedBlood:setName(cmpTestTubeWithInfectedBlood:getName().." (V-, A+)");
				end
			 else
				if not iMod.IsAntibodied then	-- заражён, нет устойчивых антител
					iMod.BloodCellsChance = 30;
					cmpTestTubeWithInfectedBlood:setName(cmpTestTubeWithInfectedBlood:getName().." (V+["..math.floor(item:getModData().InfectionRate*100).."%], A-)");
				 else							-- заражён, есть устойчивые антитела
					iMod.BloodCellsChance = 40;
					cmpTestTubeWithInfectedBlood:setName(cmpTestTubeWithInfectedBlood:getName().." (V+["..math.floor(item:getModData().InfectionRate*100).."%], A+)");
				end
			end
			cmpTestTubeWithInfectedBlood:setCustomName(true);
			player:getInventory():AddItem(cmpTestTubeWithInfectedBlood)
		end
		break;
	end
end

-- Select and inject the vaccine
function LabRecipes_InjectVaccine(items, result, player)
	local inv = player:getInventory();
	for i = 0, items:size()-1 do
		local obj = items:get(i);
		if string.find(obj:getType(),"CmpSyringeReusable") then
			inv:AddItem("LabItems.LabSyringeReusableUsed");
		elseif string.find(obj:getType(),"CmpSyringeWith") then
			inv:AddItem("LabItems.LabSyringeUsed");
		end
		if vaccineEffect[obj:getType()] then
			LabRecipes_DoInjection(player, vaccineEffect[obj:getType()]);
		end
	end
end

-- Adds dirty Flasks and Test Tubes on use
function LabRecipes_AddDirtyVials(items, result, player)
	local inv = player:getInventory();
	for i = 0, items:size()-1 do
		if string.find(items:get(i):getType(),"CmpTestTubeWithInfected") then
			inv:AddItem("LabItems.LabTestTubeDirty");
		elseif string.find(items:get(i):getType(),"CmpFlaskWith") then
			inv:AddItem("LabItems.LabFlaskDirty");
		end
	end
end

-- Adds more items for centerifuge
function LabRecipes_AddMoreBloodComponents(items, result, player)
	local zReBloodCellsChance = 0;
	for i = 0, items:size()-1 do
		local item = items:get(i);
		if item:getType() == "CmpTestTubeWithInfectedBlood" then
			local iMod = item:getModData().BloodCellsChance;
			if not iMod then
				iMod = -20;
			end
			zReBloodCellsChance = zReBloodCellsChance + iMod;
		end
	end
	if zReBloodCellsChance > ZombRand(100) then
		player:getInventory():AddItem("LabItems.CmpFlaskWithBloodCells", 1);
		player:getInventory():AddItem("LabItems.CmpFlaskWithBloodPlasma", 1);
	 else
		player:getInventory():AddItem("LabItems.CmpFlaskWithBloodPlasma", 1);
		player:getInventory():AddItem("LabItems.CmpFlaskWithBloodPlasma", 1);
	end
	LabRecipes_AddDirtyVials(items, result, player);
end
-- KERHUS READ VACCINE NOTEBOOKS --------------------------
function OnCreateLabResearchNotebookRead1(items, result, player)
	local zreVAC2ResearchBegin = player:getModData().zReVAC2ResearchBegin;
	if (zreVAC2ResearchBegin == nil) or (zreVAC2ResearchBegin == 0) then
		player:learnRecipe("LabResearchToVaccineTo2lvl");
		player:learnRecipe("ChmSynthesizePlainVaccine");
		player:getModData().zReVAC2ResearchBegin = 1;
	end
end
function OnCreateLabResearchNotebookRead2(items, result, player)
	local zreVAC2ResearchBegin = player:getModData().zReVAC2ResearchBegin;
	if (zreVAC2ResearchBegin == nil) or (zreVAC2ResearchBegin < 2) then
		player:getKnownRecipes():remove("LabResearchToVaccineTo2lvl");
		player:learnRecipe("LabResearchToVaccineTo3lvl");
		player:learnRecipe("ChmSynthesizePlainVaccine");
		player:learnRecipe("ChmSynthesizeQualityVaccine");
		player:getModData().zReVAC2ResearchBegin = 2;
	end
end
function OnCreateLabResearchNotebookRead3(items, result, player)
	local zreVAC2ResearchBegin = player:getModData().zReVAC2ResearchBegin;
	if (zreVAC2ResearchBegin == nil) or (zreVAC2ResearchBegin < 3) then
		player:getKnownRecipes():remove("LabResearchToVaccineTo2lvl");
		player:getKnownRecipes():remove("LabResearchToVaccineTo3lvl");
		player:learnRecipe("LabResearchToVaccineTo4lvl");
		player:learnRecipe("ChmSynthesizePlainVaccine");
		player:learnRecipe("ChmSynthesizeQualityVaccine");
		player:learnRecipe("ChmSynthesizeCure");
		player:getModData().zReVAC2ResearchBegin = 3;
	end
end
function OnCreateLabResearchNotebookRead4(items, result, player)
	local zreVAC2ResearchBegin = player:getModData().zReVAC2ResearchBegin;
	if (zreVAC2ResearchBegin == nil) or (zreVAC2ResearchBegin < 4) then
		player:getKnownRecipes():remove("LabResearchToVaccineTo2lvl");
		player:getKnownRecipes():remove("LabResearchToVaccineTo3lvl");
		player:getKnownRecipes():remove("LabResearchToVaccineTo4lvl");
		player:learnRecipe("ChmSynthesizePlainVaccine");
		player:learnRecipe("ChmSynthesizeQualityVaccine");
		player:learnRecipe("ChmSynthesizeCure");
		player:learnRecipe("ChmSynthesizeSerum");
		player:getModData().zReVAC2ResearchBegin = 4;
	end
end
-- KERHUS UPGRADE VACCINE ---------------------------------
function LabRecipes_ResearchNotebookVaccineStart(items, result, player)
	local zreVAC2ResearchBegin = player:getModData().zReVAC2ResearchBegin;
	local inv = player:getInventory();

	if  (zreVAC2ResearchBegin == nil) or (zreVAC2ResearchBegin == 0) then
		player:learnRecipe("LabResearchToVaccineTo2lvl");
		inv:AddItem("LabBooks.bkNotebookVaccineStart");
		player:getModData().zReVAC2ResearchBegin = 1;
	 elseif player:getModData().zReVAC2ResearchBegin == 1 then
		inv:AddItem("LabBooks.bkNotebookVaccineStart");
	 elseif player:getModData().zReVAC2ResearchBegin == 2 then
		inv:AddItem("LabBooks.bkNotebookVaccine2");
	 elseif player:getModData().zReVAC2ResearchBegin == 3 then
		inv:AddItem("LabBooks.bkNotebookVaccine3");
	 elseif player:getModData().zReVAC2ResearchBegin == 4 then
		inv:AddItem("LabBooks.bkNotebookVaccine4");
	end
end
function LabRecipes_UpgradeVaccine1(items, result, player)	-- 1) Улучшение до 2 ур
	local inv = player:getInventory();
	local zreResearchStep = 0;
	local zreFirstAid = player:getPerkLevel(Perks.Doctor);
	local zreFirstAidBonus = 0;
	local zreIsDoctor = player:getDescriptor():getProfession() == "doctor";
	local zreResearchChance1 = player:getModData().zReResearchChance1;

	for i = 0, items:size()-1 do
		if string.find(items:get(i):getType(),"CmpSyringeReusable") then
			inv:AddItem("LabItems.LabSyringeReusableUsed");
		elseif string.find(items:get(i):getType(),"CmpSyringeWith") then
			inv:AddItem("LabItems.LabSyringeUsed");
		end
	end
	LabRecipes_AddDirtyVials(items, result, player);
	
	if zreFirstAid >= 3 then	-- 
		zreFirstAidBonus = math.ceil(((zreFirstAid*zreFirstAid)/4));			-- ~ +3 +4 +7 +9 +13 +16 +21 +25
	end
	if zreResearchChance1 == nil then
		zreResearchChance1 = ZombRand(8,15);
	end
	local zReTotalChance = zreResearchChance1 + zreFirstAidBonus + (zreIsDoctor and 10 or 0);
	-- RESEARCH TYPE
	local researchType = SandboxVars.zReV2.ResearchType;
	local rResearchRand = SandboxVars.zReV2.ResearchRand;
	local researchRand = ZombRand(rResearchRand, rResearchRand*2+1);
	if researchType == 1 then -- Upgratefull Chance
		if zReTotalChance < ZombRand(100) then
			inv:AddItem("LabBooks.bkNotebookVaccineStart");
			player:getModData().zReResearchChance1 = zreResearchChance1 + researchRand;
			player:Say(getText("IGUI_zReVaccineResearchProgress"));
		 else
			inv:AddItem("LabBooks.bkNotebookVaccine2");
			player:getModData().zReResearchChance1 = 105;
			player:Say(getText("IGUI_zReVaccineResearchSucces"));
			player:learnRecipe("LabResearchToVaccineTo3lvl");
			player:learnRecipe("ChmSynthesizeQualityVaccine");
			player:getModData().zReVAC2ResearchBegin = 2;
			player:getKnownRecipes():remove("LabResearchToVaccineTo2lvl");
		end
	 elseif researchType == 2 then	-- Static Chance
		if 30 < ZombRand(100) then
			inv:AddItem("LabBooks.bkNotebookVaccineStart");
			player:Say(getText("IGUI_zReVaccineResearchUnSucces"));
		 else
			inv:AddItem("LabBooks.bkNotebookVaccine2");
			player:getModData().zReResearchChance1 = 105;
			player:Say(getText("IGUI_zReVaccineResearchSucces"));
			player:learnRecipe("LabResearchToVaccineTo3lvl");
			player:learnRecipe("ChmSynthesizeQualityVaccine");
			player:getModData().zReVAC2ResearchBegin = 2;
			player:getKnownRecipes():remove("LabResearchToVaccineTo2lvl");
		end
	 else -- researchType == 3	-- Fill Research
		if zReTotalChance < 100 then
			inv:AddItem("LabBooks.bkNotebookVaccineStart");
			player:getModData().zReResearchChance1 = zreResearchChance1 + researchRand;
			player:Say(getText("IGUI_zReVaccineResearchProgress"));
		 else
			inv:AddItem("LabBooks.bkNotebookVaccine2");
			player:getModData().zReResearchChance1 = 105;
			player:Say(getText("IGUI_zReVaccineResearchSucces"));
			player:learnRecipe("LabResearchToVaccineTo3lvl");
			player:learnRecipe("ChmSynthesizeQualityVaccine");
			player:getModData().zReVAC2ResearchBegin = 2;
			player:getKnownRecipes():remove("LabResearchToVaccineTo2lvl");
		end
	end
end
function LabRecipes_UpgradeVaccine2(items, result, player)	-- 2) Улучшение до 3 ур
	local inv = player:getInventory();
	local zreResearchStep = 0;
	local zreFirstAid = player:getPerkLevel(Perks.Doctor);
	local zreFirstAidBonus = 0;
	local zreIsDoctor = player:getDescriptor():getProfession() == "doctor";
	local zreResearchChance2 = player:getModData().zReResearchChance2;

	for i = 0, items:size()-1 do
		if string.find(items:get(i):getType(),"CmpSyringeReusable") then
			inv:AddItem("LabItems.LabSyringeReusableUsed");
		elseif string.find(items:get(i):getType(),"CmpSyringeWith") then
			inv:AddItem("LabItems.LabSyringeUsed");
		end
	end
	LabRecipes_AddDirtyVials(items, result, player);

	if zreFirstAid >= 5 then	-- 
		zreFirstAidBonus = math.ceil((-4) + ((zreFirstAid*zreFirstAid)/4.5));	-- ~ +2 +4 +7 +11 +14 +19
	end
	if zreResearchChance2 == nil then
		zreResearchChance2 = ZombRand(6,13);
	end
	local zReTotalChance = zreResearchChance2 + zreFirstAidBonus + (zreIsDoctor and 10 or 0)
	-- RESEARCH TYPE
	local researchType = SandboxVars.zReV2.ResearchType;
	local rResearchRand = SandboxVars.zReV2.ResearchRand;
	local researchRand = ZombRand(math.ceil(rResearchRand*0.75), math.ceil(rResearchRand*1.5+1));
	if researchType == 1 then -- Upgratefull Chance
		if zReTotalChance < ZombRand(100) then
			inv:AddItem("LabBooks.bkNotebookVaccine2");
			player:getModData().zReResearchChance2 = zreResearchChance2 + researchRand;
			player:Say(getText("IGUI_zReVaccineResearchProgress"));
		 else
			inv:AddItem("LabBooks.bkNotebookVaccine3");
			player:getModData().zReResearchChance2 = 105;
			player:Say(getText("IGUI_zReVaccineResearchSucces"));
			player:learnRecipe("LabResearchToVaccineTo4lvl");
			player:learnRecipe("ChmSynthesizeCure");
			player:getModData().zReVAC2ResearchBegin = 3;
			player:getKnownRecipes():remove("LabResearchToVaccineTo3lvl");
		end
	 elseif researchType == 2 then -- Static Chance
		if 20 < ZombRand(100) then
			inv:AddItem("LabBooks.bkNotebookVaccine2");
			player:Say(getText("IGUI_zReVaccineResearchUnSucces"));
		 else
			inv:AddItem("LabBooks.bkNotebookVaccine3");
			player:getModData().zReResearchChance2 = 105;
			player:Say(getText("IGUI_zReVaccineResearchSucces"));
			player:learnRecipe("LabResearchToVaccineTo4lvl");
			player:learnRecipe("ChmSynthesizeCure");
			player:getModData().zReVAC2ResearchBegin = 3;
			player:getKnownRecipes():remove("LabResearchToVaccineTo3lvl");
		end
	 else -- researchType == 3 -- Fill Research
		if zReTotalChance < 100 then
			inv:AddItem("LabBooks.bkNotebookVaccine2");
			player:getModData().zReResearchChance2 = zreResearchChance2 + researchRand;
			player:Say(getText("IGUI_zReVaccineResearchProgress"));
		 else
			inv:AddItem("LabBooks.bkNotebookVaccine3");
			player:getModData().zReResearchChance2 = 105;
			player:Say(getText("IGUI_zReVaccineResearchSucces"));
			player:learnRecipe("LabResearchToVaccineTo4lvl");
			player:learnRecipe("ChmSynthesizeCure");
			player:getModData().zReVAC2ResearchBegin = 3;
			player:getKnownRecipes():remove("LabResearchToVaccineTo3lvl");
		end
	end
end
function LabRecipes_UpgradeVaccine3(items, result, player)	-- 3) Улучшение до 4 ур
	local inv = player:getInventory();
	local zreResearchStep = 0;
	local zreFirstAid = player:getPerkLevel(Perks.Doctor);
	local zreFirstAidBonus = 0;
	local zreIsDoctor = player:getDescriptor():getProfession() == "doctor";
	local zreResearchChance3 = player:getModData().zReResearchChance3;

	for i = 0, items:size()-1 do
		if string.find(items:get(i):getType(),"CmpSyringeReusable") then
			inv:AddItem("LabItems.LabSyringeReusableUsed");
		elseif string.find(items:get(i):getType(),"CmpSyringeWith") then
			inv:AddItem("LabItems.LabSyringeUsed");
		end
	end
	LabRecipes_AddDirtyVials(items, result, player);

	if zreFirstAid >= 7 then	-- 
		zreFirstAidBonus = math.ceil((-7) + ((zreFirstAid*zreFirstAid)/6));		-- ~ +2 +4 +8 +10
	end
	if zreResearchChance3 == nil then
		zreResearchChance3 = ZombRand(4,11);
	end
	local zReTotalChance = zreResearchChance3 + zreFirstAidBonus + (zreIsDoctor and 10 or 0);
	-- RESEARCH TYPE
	local researchType = SandboxVars.zReV2.ResearchType;
	local rResearchRand = SandboxVars.zReV2.ResearchRand;
	local researchRand = ZombRand(math.ceil(rResearchRand*0.5), rResearchRand+1);
	if researchType == 1 then -- Upgratefull Chance
		if zReTotalChance < ZombRand(100) then
			inv:AddItem("LabBooks.bkNotebookVaccine3");
			player:getModData().zReResearchChance3 = zreResearchChance3 + researchRand;
			player:Say(getText("IGUI_zReVaccineResearchProgress"));
		 else
			inv:AddItem("LabBooks.bkNotebookVaccine4");
			player:getModData().zReResearchChance3 = 105;
			player:Say(getText("IGUI_zReVaccineResearchSucces"));
			player:learnRecipe("ChmSynthesizeSerum");
			player:getModData().zReVAC2ResearchBegin = 4;
			player:getKnownRecipes():remove("LabResearchToVaccineTo4lvl");
		end
	 elseif researchType == 2 then	-- Static Chance
		if 14 < ZombRand(100) then
			inv:AddItem("LabBooks.bkNotebookVaccine3");
			player:Say(getText("IGUI_zReVaccineResearchUnSucces"));
		 else
			inv:AddItem("LabBooks.bkNotebookVaccine4");
			player:getModData().zReResearchChance3 = 105;
			player:Say(getText("IGUI_zReVaccineResearchSucces"));
			player:learnRecipe("ChmSynthesizeSerum");
			player:getModData().zReVAC2ResearchBegin = 4;
			player:getKnownRecipes():remove("LabResearchToVaccineTo4lvl");
		end
	 else -- researchType == 3	-- Fill Research
		if zReTotalChance < 100 then
			inv:AddItem("LabBooks.bkNotebookVaccine3");
			player:getModData().zReResearchChance3 = zreResearchChance3 + researchRand;
			player:Say(getText("IGUI_zReVaccineResearchProgress"));
		 else
			inv:AddItem("LabBooks.bkNotebookVaccine4");
			player:getModData().zReResearchChance3 = 105;
			player:Say(getText("IGUI_zReVaccineResearchSucces"));
			player:learnRecipe("ChmSynthesizeSerum");
			player:getModData().zReVAC2ResearchBegin = 4;
			player:getKnownRecipes():remove("LabResearchToVaccineTo4lvl");
		end
	end
end

-- KERHUS CREATE VACCINE ---------------------------------
function LabRecipes_CreateVaccine1lvl(items, result, player)
	LabRecipes_AddDirtyVials(items, result, player);
	local inv = player:getInventory();
	for i = 0, items:size()-1 do
		if (items:get(i):getType() == "LabSyringeReusable") then
			inv:AddItem("LabItems.CmpSyringeReusableWithPlainVaccine");
		 elseif (items:get(i):getType() == "LabSyringe") then
			inv:AddItem("LabItems.CmpSyringeWithPlainVaccine");
		end
	end
end
function LabRecipes_CreateVaccine2lvl(items, result, player)
	LabRecipes_AddDirtyVials(items, result, player);
	local inv = player:getInventory();
	for i = 0, items:size()-1 do
		if (items:get(i):getType() == "LabSyringeReusable") then
			inv:AddItem("LabItems.CmpSyringeReusableWithQualityVaccine");
		 elseif (items:get(i):getType() == "LabSyringe") then
			inv:AddItem("LabItems.CmpSyringeWithQualityVaccine");
		end
	end
end
function LabRecipes_CreateVaccine3lvl(items, result, player)
	LabRecipes_AddDirtyVials(items, result, player);
	local inv = player:getInventory();
	for i = 0, items:size()-1 do
		if (items:get(i):getType() == "LabSyringeReusable") then
			inv:AddItem("LabItems.CmpSyringeReusableWithCure");
		 elseif (items:get(i):getType() == "LabSyringe") then
			inv:AddItem("LabItems.CmpSyringeWithCure");
		end
	end
end
function LabRecipes_CreateVaccine4lvl(items, result, player)
	LabRecipes_AddDirtyVials(items, result, player);
	local inv = player:getInventory();
	for i = 0, items:size()-1 do
		if (items:get(i):getType() == "LabSyringeReusable") then
			inv:AddItem("LabItems.CmpSyringeReusableWithSerum");
		 elseif (items:get(i):getType() == "LabSyringe") then
			inv:AddItem("LabItems.CmpSyringeWithSerum");
		end
	end
end


------------------------------------------------------------------------------------------
---------------------------------------- OnGiveXp ----------------------------------------

function LabRecipes_GiveXP_Small(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.Woodwork, 3);
	player:getXp():AddXP(Perks.Electricity, 5);
end

function LabRecipes_GiveXP_Medium(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.Woodwork, 6);
	player:getXp():AddXP(Perks.Electricity, 6);
end

function LabRecipes_GiveXP_Medium2(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.MetalWelding, 8);
	player:getXp():AddXP(Perks.Electricity, 8);
end

function LabRecipes_GiveXP_Large(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.Woodwork, 12);
	player:getXp():AddXP(Perks.Electricity, 12);
end

function LabRecipes_GiveXP_Woodwork_5(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.Woodwork, 5);
end

function LabRecipes_GiveXP_Doctor_1(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.Doctor, 1);
end

function LabRecipes_GiveXP_Doctor_2(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.Doctor, 2);
end

function LabRecipes_GiveXP_Doctor_3(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.Doctor, 3);
end

function LabRecipes_GiveXP_Doctor_5(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.Doctor, 5);
end

function LabRecipes_GiveXP_Doctor_10(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.Doctor, 10);
end

function LabRecipes_GiveXP_Doctor_15(recipe, ingredients, result, player)
	player:getXp():AddXP(Perks.Doctor, 15);
end

---------------------------------------- Inventory Context Menus ----------------------------------------

-- Cleaning tainted water
local function LabRecipes_CMOnClearTaintedWater(item, player, items, tabletsNeed) -- testo
	ISInventoryPaneContextMenu.transferIfNeeded(player, item);
	ISInventoryPaneContextMenu.equipWeapon(item, true, false, player:getPlayerNum());
	ISTimedActionQueue.add(LabActionCleanWater:new(player, item, tabletsNeed));
end

-- Taking Albumin pills
local function LabRecipes_CMOnTakeAlbuminPills(item, player, items)	-- testo
	ISInventoryPaneContextMenu.transferIfNeeded(player, item);
	ISTimedActionQueue.add(LabActionTakeAlbumin:new(player, item));
end

-- Add some options to items menu
local function LabRecipes_BuildInventoryCM(player, context, items) -- testo
	local item = nil;
	for _, v in ipairs(items) do
		if instanceof(v, "InventoryItem") then
			item = v
		else
			item = v.items[1];
		end
		if item:isWaterSource() then
			if item:isTaintedWater() then
				local tablets = LabRecipes_CountDrainablePieces(getSpecificPlayer(player):getInventory(), "CmpChlorineTablets");
				if tablets > 0 then
					local tabletsNeed = chlorineDose[item:getType()];
					if tabletsNeed == nil then
						tabletsNeed = chlorineDose.Default;
					end
					local option = context:addOption(getText("ContextMenu_LabClearWater"), item, LabRecipes_CMOnClearTaintedWater, getSpecificPlayer(player), items, tabletsNeed);
					local tooltip = ISInventoryPaneContextMenu.addToolTip();
					tooltip.description = getText("ContextMenu_LabClearWaterTooltip", string.format(" <%s> %d", tabletsNeed>tablets and "RED" or "GREEN", tabletsNeed), tablets);
					option.toolTip = tooltip;
					option.notAvailable = tabletsNeed>tablets;
				end
			end
		end
		if item:getType() == "CmpAlbuminPills" then
			context:addOption(getText("ContextMenu_Take_pills"), item, LabRecipes_CMOnTakeAlbuminPills, getSpecificPlayer(player), items);
		end
	end
end


---------------------------------------- World Context Menus ----------------------------------------

-- ...
local function predicateNotBroken(item)
	return not item:isBroken();
end

--- ...
local function wearIfNeeded(player, clothing)
	if not(player:isEquippedClothing(clothing)) then
		ISInventoryPaneContextMenu.wearItem(clothing, player:getPlayerNum());
	end
end

-- Make an Autopsy
local function LabRecipes_WMOnCorpseAutopsy(player, corpse, square, bottom) -- testo
	if (corpse and luautils.walkAdj(player, corpse:getSquare())) or luautils.walkAdj(player, bottom:getSquare()) then
		local inv = player:getInventory();
		--local mask = inv:getFirstTypeEvalRecurse("Hat_SurgicalMask_Blue", predicateNotBroken) or inv:getFirstTypeEvalRecurse("Hat_SurgicalMask_Green", predicateNotBroken) or inv:getFirstTypeEvalRecurse("Hat_DustMask", predicateNotBroken) or inv:getFirstTypeEvalRecurse("Hat_GasMask", predicateNotBroken) or inv:getFirstTypeEvalRecurse("Hat_BandanaMaskRed", predicateNotBroken) or inv:getFirstTypeEvalRecurse("Hat_BandanaMaskDesert", predicateNotBroken);
		local scalpel = inv:getFirstTypeEvalRecurse("Scalpel", predicateNotBroken);
		--wearIfNeeded(player, mask);
		if scalpel and scalpel.isRequiresEquippedBothHands then										-- Fix by Yummy
            ISInventoryPaneContextMenu.equipWeapon(scalpel, true, false, player:getPlayerNum());
            ISTimedActionQueue.add(LabActionMakeAutopsy:new(player, corpse, square, bottom));
		end
	end
end

-- Add check line to Option tooltip
local function LabRecipes_CreateCheckTooltip(option, inventory, moduleName, itemTypes, count, noBroken) -- testo
	local n = 0;
	for _, v in ipairs(itemTypes) do
		if noBroken then
			n = n+inventory:getCountTypeEvalRecurse(v,predicateNotBroken);
		else
			n = n+inventory:getItemCountRecurse(v);
		end
	end
	local s = moduleName.."."..itemTypes[1];
	if count == 1 then
		option.toolTip.description = option.toolTip.description..string.format(" -  <%s> %s <RGB:1,1,1> <LINE>", (n<count) and "RED" or "GREEN", getItemNameFromFullType(s));
	else
		option.toolTip.description = option.toolTip.description..string.format(" -  <%s> %s ( %d / %d ) <RGB:1,1,1> <LINE>", (n<count) and "RED" or "GREEN", getItemNameFromFullType(s), math.min(n,count), count);
	end
	return n >= count;
end

-- Create corpse autopsy option tooltip
local function LabRecipes_CreateCorpseAutopsyTooltip(option, inventory, notFresh, notZombie, notOrgans)	-- testo
	local tooltip = ISInventoryPaneContextMenu.addToolTip();
	option.toolTip = tooltip;
	--tooltip.description = tooltip.description..string.format("%s:  <%s> %s12 %s <LINE> <RGB:1,1,1>", getText("ContextMenu_LabCorpseAge"), notFresh and "RED" or "GREEN", notFresh and ">" or "<", getText("ContextMenu_LabHrs"));
	tooltip.description = tooltip.description..string.format("%s:  <%s> %s <LINE> <RGB:1,1,1>", getText("ContextMenu_LabCorpseAge"), notFresh and "RED" or "GREEN", notFresh and getText("ContextMenu_LabYes") or getText("ContextMenu_LabNo"));
	tooltip.description = tooltip.description..string.format("%s:  <%s> %s <LINE> <RGB:1,1,1>", getText("ContextMenu_LabWasZombie"), notZombie and "RED" or "GREEN", notZombie and getText("ContextMenu_LabNo") or getText("ContextMenu_LabYes"));
	tooltip.description = tooltip.description..string.format("%s:  <%s> %s <LINE> <RGB:1,1,1>", getText("ContextMenu_LabAutopsyDone"), notOrgans and "RED" or "GREEN", notOrgans and getText("ContextMenu_LabYes") or getText("ContextMenu_LabNo"));
	tooltip.description = tooltip.description..getText("ContextMenu_LabMustHaveItems");
	local ok = true;
	--ok = LabRecipes_CreateCheckTooltip(option, inventory, "Base", {"Hat_DustMask","Hat_SurgicalMask_Blue", "Hat_SurgicalMask_Green","Hat_BandanaMaskRed","Hat_BandanaMaskDesert","Hat_GasMask"}, 1, true) and ok;
	ok = LabRecipes_CreateCheckTooltip(option, inventory, "Base", {"Scalpel"}, 1, true) and ok;
	ok = LabRecipes_CreateCheckTooltip(option, inventory, "Base", {"Tweezers"}, 1) and ok;
	option.notAvailable = notFresh or notZombie or notOrgans or not(ok);
end

--- Create common style tooltip
local function LabRecipes_CreateCommonTooltip(option) -- testo
	local tooltip = ISInventoryPaneContextMenu.addToolTip();
	option.toolTip = tooltip;
	tooltip.description = tooltip.description..getText("ContextMenu_LabMustHaveItems");
end

-- Add some options to world menu
local function LabRecipes_BuildZombieWM(playerNum, context, worldobjects, test)	-- testo
	if test and ISWorldObjectContextMenu.Test then
		return true;
	end
	local player = getSpecificPlayer(playerNum);
	local inv = player:getInventory();
	local square = player:getCurrentSquare();
	local subMenu = nil;
	for _, v in ipairs(worldobjects) do
		local sq = v:getSquare();
		if sq then
			
			for y = sq:getY()-1, sq:getY()+1 do
				for x = sq:getX()-1, sq:getX()+1 do
					local square = getCell():getGridSquare(x, y, sq:getZ());
					if not(square) then
						break;
					end
					for i = 0, square:getStaticMovingObjects():size()-1 do
						local obj = square:getStaticMovingObjects():get(i);
						if instanceof(obj, "IsoDeadBody") then
							if not(subMenu) then
								subMenu = ISContextMenu:getNew(context);
								context:addSubMenu(context:addOption(getText("ContextMenu_LabCorpseAutopsy"), worldobjects, nil), subMenu);
							end
							local notFresh = obj:isSkeleton();
							local notZombie = not(obj:isZombie());
							local notOrgans = obj:getModData().Autopsy;
							local option = subMenu:addOption(getText("ContextMenu_LabCorpse"), player, LabRecipes_WMOnCorpseAutopsy, obj, square, nil, nil);
							LabRecipes_CreateCorpseAutopsyTooltip(option, inv, notFresh, notZombie, notOrgans);
						end
					end
				end
			end
		end
		break;
	end
end

---------------------------------------- Events ----------------------------------------

Events.OnFillInventoryObjectContextMenu.Add(LabRecipes_BuildInventoryCM);
Events.OnFillWorldObjectContextMenu.Add(LabRecipes_BuildZombieWM);