class 'InventoryBase' (Node)

local CELL_INDEX = "InvCellIndex_"
local CELL_FREE_INDEX = "InvCellFreeIndex"
local INVENTORY_SHIFT = "InventoryShift"

function InventoryBase:__init(panel, order)
	Node.__init(self, panel:getMainEntity(), 'Inventory', order or 4)
	self.game_scene = panel.game_scene
	
	-- Настройки инвентаря по-умолчанию
	do
		if not self.settings then
			self.settings = {}
		end
	
		local function init_value(variable, value)
			if not self.settings[variable] then
				self.settings[variable] = value
			end
		end
	
		init_value('DROP_MOVE_TIME', 0.3)
		init_value('SLIDE_TIME', 0.6)

		init_value('TOOL_NAME_TEXT_OFFSET_X', 10)
		init_value('TOOL_NAME_TEXT_OFFSET_Y', -30)

		init_value('TOOL_COUNTER_OFFSET', {x = 0, y = 30})
		init_value('TOOL_COUNTER_FONT_SCALE', 0.8) --the font itself is TEXT_FONT

		init_value('TOOL_SCALE_IN_INVENTORY', 1)
		init_value('ROOT_SHOW_POSITION', {x = 0, y = 3})
		init_value('ROOT_SLIDE_POSITION', {x = 0, y = 75})
		init_value('ROOT_HIDE_POSITION', {x = 0, y = 128})

		init_value('SLIDE_INV_TIME', 0.4)
		init_value('SLIDE_INV_DELAY', 1.1)

		init_value('ANCHOR_SCISSORS_TOP_X', 276)
		init_value('ANCHOR_SCISSORS_TOP_Y', 690)
		init_value('ANCHOR_SCISSORS_BOTTOM_X', 742)

		init_value('START_ANCHOR_POS', {x = 308, y = 726})
		init_value('CELLS_WIDTH', 68)
		init_value('VISIBLE_CELLS_COUNT', 7)

		init_value('TEXT_FONT', 'Data/Fonts/Options.fnt')
	end

	--  
	self.tools = {}
	self.tool_in_hand   = nil
	self.active_tools   = {}
	self.locked_tools   = {}
	self.flying_tools   = {}
	
	self.cells          = {}
	self.cells_free_id  = 0
	
	self.anchor         = nil
	self.anchorAux      = nil
	self.anchor_col     = 0
	self.anchor_colAux  = self.anchor_col    
	self.slideEff       = nil
			
	self.hintedTool = nil
	
	-- Создаём таймер
	self.timer = Timer()
	
	-- Заставляем вызываться OnUpdate по сигналу
	local function update(entity, dt)
		self:onUpdate(dt)
	end
	-- пробуем повесить таймер на апдейт MainEntity, неясно зачем его повесили на апдейт мейненти геймсцены, которая активна
	-- после GameScene::stop()
	--self.game_scene:getWindow().onUpdate:add(update) 
	self:getMainEntity().onUpdate:add(update)
	
	-- Загружаем ресурсы
	if self.CreateLoaderTable then
		local table_resource = self:CreateLoaderTable()
		LoadScene(self.main_entity, table_resource)
	end

	-- Кнопки скролла инвентаря
	self.btn_left	= self:getEntityNamed('InvLeftButton')
	self.btn_right	= self:getEntityNamed('InvRightButton')
	self.btn_left:setSendEventsToParent(true)
	self.btn_right:setSendEventsToParent(true)
	
	local function on_left_button()
		self:slideInventoryAt(1)
	end
	self.btn_left.onPress:add(on_left_button)
	
	local function on_right_button()
		self:slideInventoryAt(-1)
	end
	self.btn_right.onPress:add(on_right_button)
	
	self:getMainEntity():getEntityNamed('InvLeftButton'):setSFXTrackOnPress( getCommonSoundEntity('ButtonClick') )
	self:getMainEntity():getEntityNamed('InvRightButton'):setSFXTrackOnPress( getCommonSoundEntity('ButtonClick') )

	-- Кнопки для закрепления инвентаря. Работает только если есть соотв. ентити
	if self:hasEntityNamed('LockButton') then
		self.btn_lock = self:getEntityNamed('LockButton')
		self.btn_unlock = self:getEntityNamed('UnlockButton')
	
		if HasGameState(INVENTORY_LOCK) then
			self.btn_lock:hide()
		else
			self.btn_unlock:hide()
		end
	
		local function on_lock_button()
			self:lockInventory()

			self.btn_lock:hide()
			self.btn_unlock:show()
		
			if self:getPanel():getMode() == EPanelMode.QUEST then
				self:slideUp(0.4)
			end
		end
		self.btn_lock.onPress:add(on_lock_button)
	
		local function on_unlock_button()
			self:unlockInventory()

			self.btn_unlock:hide()
			self.btn_lock:show()
			
			self:slideDown(0.4)
		end
		self.btn_unlock.onPress:add(on_unlock_button)
		
		self:getMainEntity():getEntityNamed('LockButton'):setSFXTrackOnPress( getCommonSoundEntity('ButtonClick') )
		self:getMainEntity():getEntityNamed('UnlockButton'):setSFXTrackOnPress( getCommonSoundEntity('ButtonClick') )
	end

	-- Дополнительные элементы для стандартного инвентаря
	if self:hasEntityNamed('InventoryRoot') then
		-- Root - для того, чтобы таскать элементы интвентаря за ним
		self.root = self:getEntityNamed('InventoryRoot')
		self.root:setAnimationSize(0, 0)
		self.root:setBoundingBox(Rectanglef(0, 0, 0, 0))
		self.root:setScrXY(self.settings.ROOT_SHOW_POSITION.x, self.settings.ROOT_SHOW_POSITION.y)
		self.root:setHandlingChildEvents(true)
		self.root.onFocusIn:add(bind(self.onFocusInInventoryPanel, self))
		self.root.onFocusOut:add(bind(self.onFocusOutInventoryPanel, self))
		
		-- Scissors - для отсечения области рендера тулзов
		self.anchor_scs = self:getEntityNamed('AnchorScissors')
		self.anchor_scs:setScissorBox(self.settings.ANCHOR_SCISSORS_TOP_X, self.settings.ANCHOR_SCISSORS_TOP_Y, self.settings.ANCHOR_SCISSORS_BOTTOM_X, WORK_H)
		self.anchor_scs:setAnimationSize(2000, 2000)
		self.anchor_scs:setColor(Colorf(0, 0, 0, 0))
		
		-- Anchors - к нему крепятся ячейки с тулзами
		self.anchor = self:getEntityNamed('ToolsAnchor')
		self.anchor:setAnimationSize(0, 0)
		self.anchor:setXY(self.settings.START_ANCHOR_POS.x + self.anchor_col * self.settings.CELLS_WIDTH, self.settings.START_ANCHOR_POS.y)

		-- Вспомогательный якорь
		self.anchorAux = self:getEntityNamed('ToolsAnchorAux')
		self.anchorAux:setAnimationSize(0, 0)
		self.anchorAux:setXY(self.settings.START_ANCHOR_POS.x + self.anchor_colAux * self.settings.CELLS_WIDTH, self.settings.START_ANCHOR_POS.y)
	end
	   
	-- Текст подсказка при наведении на тулзу
	self.hintText = self:getEntityNamed('HintOnTool')
	self.hintText:setVisible(false)
	
	-- Создание видимых ячеек инвентаря
	for i = 1, self.settings.VISIBLE_CELLS_COUNT do
		self:createCell()
	end
	
	-- Загружаем все тулзы
	self:registerAllTools()
	
	-- Ставим root в нижнее положение (частично за экраном)
	if self:hasEntityNamed('InventoryRoot') then
		self.root:setScrXY(self.settings.ROOT_SLIDE_POSITION.x, self.settings.ROOT_SLIDE_POSITION.y)
	end
	
	if HasGameState(INVENTORY_LOCK) then
		self:slideUp()
	else
		self:slideDown()
	end
end
-------------------------------------------------------------------------------

function InventoryBase:postInit()
	-- Сбрасываем тулзу, если кликаем по инвентарю или панели
	local function try_to_drop_tool()
		if self.tool_in_hand then
			self:dropTool()
		end
	end
	
	self:getMainEntity():setHandlingChildEvents(true)
	self:getMainEntity().onClickBegin:add(try_to_drop_tool)
	
	self:getPanel():getControlsPanel():getMainEntity():setHandlingChildEvents(true)
	self:getPanel():getControlsPanel():getMainEntity().onClickBegin:add(try_to_drop_tool)
	
	self:getPanel():getControlsPanel():getHintAnimationLogic():getMainEntity():setHandlingChildEvents(true)
	self:getPanel():getControlsPanel():getHintAnimationLogic():getMainEntity().onClickBegin:add(try_to_drop_tool)
end
-------------------------------------------------------------------------------

function InventoryBase:getPlayField()
	return self.game_scene:getPlayField()
end
-------------------------------------------------------------------------------

function InventoryBase:getPanel()
	return self.game_scene:getPanel()
end
-------------------------------------------------------------------------------

function InventoryBase:getTimer()
	return self.timer
end
-------------------------------------------------------------------------------

function InventoryBase:createCell(tool_info)
	local index
	
	if tool_info then
		if HasGameState(CELL_INDEX..tool_info.name) then
			index = tonumber(GetGameState(CELL_INDEX..tool_info.name):get(0))
		else
			if tool_info.additional_info.collection then
				for _, name_tool in pairs(tool_info.additional_info.collection:get_tools()) do
					if HasGameState(CELL_INDEX..name_tool) then
						index = tonumber(GetGameState(CELL_INDEX..name_tool):get(0))
						break
					end
				end
			end
		
			if not index then
				if HasGameState(CELL_FREE_INDEX) then
					index = tonumber(GetGameState(CELL_FREE_INDEX):get(0))
				else
					index = 1
				end
		
				SetGameState(CELL_FREE_INDEX, tostring(index + 1))
				SetGameState(CELL_INDEX..tool_info.name, tostring(index))
			end
		end
		
		if self.cells[index] then
			local info = self.cells[index]
			info.tool_info = tool_info
			return info
		end
	else
		index = self.cells_free_id + 1
	end
	
	local cellX = (index - 1) * self.settings.CELLS_WIDTH
	local cellY = 0
	local cellZ = 1
	
	local entity = CreateSpriteEntity('Data/Textures/GUI/Inventory/inventory_cell.png')
	entity:attachTo(self.anchor)
	entity:setName('ToolHolder_'..self.cells_free_id)
	entity:setXY(cellX, cellY)
	entity:setOrder(cellZ)

	local cell_info = {
						cell_entity = entity,
						tool_info   = tool_info,
						eff         = nil,
						show        = false,
						actions     = nil
					  }
	
	self.cells[index] = cell_info
	self.cells_free_id = self.cells_free_id + 1
	
	return cell_info
end
-------------------------------------------------------------------------------

function InventoryBase:removeCell(tool_info)
	local cell_info, index = self:getCellInfoByToolInfo(tool_info)
	local cell_entity  = cell_info.cell_entity
	
	-- Открепляем ячейку и убираем её вниз
	local scrX = cell_entity:getScrX()
	local scrY = cell_entity:getScrY()
	local scrZ = cell_entity:getOrder()
	cell_entity:attachTo( self:getMainEntity() )
	cell_entity:setScrXY( scrX, scrY )
	cell_entity:setOrder( scrZ )
	cell_entity:ChangeOpacity(cell_entity:getOpacity(), 0, self.settings.SLIDE_TIME)
	cell_entity:Move(0, 100, self.settings.SLIDE_TIME/2)

	-- Удаляем все сигналы, которые связаны с ячейкой    
	if cell_info.actions then
		for i, v in pairs(cell_info.actions) do
			v:remove()
		end
		cell_info.actions = nil
	end
	
	-- Удаляем ентити ячейки, когда она уехала за экран
	local function deleteCell(cell_ent)
		cell_ent:destroy()
	end
	self.timer:add(bind(deleteCell, cell_entity), self.settings.SLIDE_TIME)
	
	--
	if (#self.cells - 1) < self.settings.VISIBLE_CELLS_COUNT then
		self:createCell()
	end
	
	self.anchor_colAux = self.anchor_col
	self.anchorAux:setX( self.anchor:getX() )
	self:attachCellsToAuxAnchor(index + 1)
		
	table.remove(self.cells, index)
	
	-- Исправление ошибки, с созданием ячеек с одинаковым именем
	local new_index = 0
	for i, v in pairs(self.cells) do
		v.cell_entity:setName('ToolHolder_'..new_index)
		new_index = new_index + 1
	end
	
	if HasGameState(CELL_INDEX..tool_info.name) then
		RemoveGameState(CELL_INDEX..tool_info.name)
	end
	
	for i = index, #self.cells do
		if self.cells[i].tool_info then
			SetGameState(CELL_INDEX..self.cells[i].tool_info.name, tostring(i))
		end
	end
	
	if HasGameState(CELL_FREE_INDEX) then
		local maxIndex = tonumber(GetGameState(CELL_FREE_INDEX):get(0))
		SetGameState(CELL_FREE_INDEX, tostring(maxIndex - 1))
	end
	
	self.cells_free_id = self.cells_free_id - 1
	
	if ((-self.anchor_col) + self.settings.VISIBLE_CELLS_COUNT - 1) ~= #self.cells then
		self:slideInventoryAt(-1, true)
	else
		self:slideInventoryAt(1, false)
	end
end

function InventoryBase:attachCellsToAuxAnchor(startIndex)
	startIndex = startIndex or 1
	local cnt = #self.cells
	
	for i = startIndex, cnt do
		local cell_entity = self.cells[i].cell_entity
		
		cell_entity:attachTo( self.anchorAux )
		cell_entity:setX( (i - 1) * self.settings.CELLS_WIDTH )
		cell_entity:setY( 0 )
	end
end

function InventoryBase:dettachCellsFromAuxAnchor()
	for i, cellInfo in ipairs(self.cells) do
		local cell_entity = cellInfo.cell_entity
		
		if cell_entity:getParent():getName() == self.anchorAux:getName() then
			cell_entity:attachTo( self.anchor )
			cell_entity:setX( (i - 1) * self.settings.CELLS_WIDTH )
			cell_entity:setY( 0 )
		end
	end
end

function InventoryBase:checkCellsOpacity(time)
	time = time or 0
	
	for index, info in ipairs(self.cells) do
		local cell_col = self.anchor_col + index - 1

		if cell_col < 0 or cell_col >= self.settings.VISIBLE_CELLS_COUNT then
			if info.show then
				info.show = false
				
				if info.eff then
					info.eff:destroy()
					info.eff = nil
				end
				
				if time == 0 then
					info.cell_entity:setOpacity(0)
				else
					info.eff = info.cell_entity:ChangeOpacity(1, 0, time)
					info.eff:setDestroysOnEnd(false)
				end
			end
		else
			if not info.show then
				info.show = true
				
				if info.eff then
					info.eff:destroy()
					info.eff = nil
				end
				
				if time == 0 then
					info.cell_entity:setOpacity(1)
				else
					info.eff = info.cell_entity:ChangeOpacity(0, 1, time)
					info.eff:setDestroysOnEnd(false)
				end
			end
		end
	end
end

function InventoryBase:addTool(tool_info)
	local tool_ent = tool_info.entities.tool_ent
	local tool_shadow_ent = tool_info.entities.tool_shadow_ent
	
	tool_ent:setScale(self.settings.TOOL_SCALE_IN_INVENTORY)
	tool_shadow_ent:setScale(self.settings.TOOL_SCALE_IN_INVENTORY)
	
	tool_ent:setVisible(true)
	tool_shadow_ent:setVisible(false)

	self.active_tools[tool_info.name] = tool_info
	self:moveToolToFreeSlot(tool_info, 0)
end

function InventoryBase:takeTool(tool_info)
	tool_info.entities.tool_ent:attachTo(self:getMainEntity())
	tool_info.entities.tool_shadow_ent:setVisible(true)
	
	self.tool_in_hand = tool_info
	if tool_info.additional_info.collection_text then
		tool_info.additional_info.collection_text:setVisible(false)
	end
	local name = self.tool_in_hand.name
	
	if tool_info.on_get then
		local event = {}
		event.toolName = name
		tool_info.on_get(event)
	end

	local eff = CreateAnimationEffector()
	eff:attachTo(tool_info.entities.tool_ent)
	eff:setMode(_ANIM_LOOP)
	eff:setKey(_TRACK_S, 0,    self.settings.TOOL_SCALE_IN_INVENTORY)
	eff:setKey(_TRACK_S, 0.65, self.settings.TOOL_SCALE_IN_INVENTORY + 0.1)
	eff:setKey(_TRACK_S, 1.,   self.settings.TOOL_SCALE_IN_INVENTORY)
	eff:play()
	
	getHandledCursor():setState('CursorTool')
	getHandledCursor():lockState()
end

function InventoryBase:dropTool(custom_move_time)
	if self.tool_in_hand == nil then
		return
	end
	
	local time = custom_move_time or self.settings.DROP_MOVE_TIME
	local name = self.tool_in_hand.name
	
	self.tool_in_hand.entities.tool_ent:destroyEffectors()
	self.tool_in_hand.entities.tool_ent:setScale(self.settings.TOOL_SCALE_IN_INVENTORY)
	
	self:moveToolToFreeSlot(self.tool_in_hand, time)
	
	self.tool_in_hand = nil
	
	if getHandledCursor():stateIsLocked() then   
		getHandledCursor():unlockState()
		getHandledCursor():setState('CursorDefault')
	end
	
	if self.tools[name].on_drop then
		local event    = {}
		event.toolName = name
		self.tools[name].on_drop(event)
	end
end

function InventoryBase:removeTool(tool_info)
	if self.tool_in_hand and self.tool_in_hand.name == tool_info.name then
		local fade_time = 0.25
		local obj = self.tool_in_hand.entities.tool_ent
		
		self.tool_in_hand.entities.tool_ent:ChangeOpacity(1, 0, fade_time)
		self.tool_in_hand = nil
		
		self.timer:add(function() obj:destroy() end, fade_time)
		
		if getHandledCursor():stateIsLocked() then
			getHandledCursor():unlockState()
			getHandledCursor():setState('CursorDefault')
		end
	else
		tool_info.entities.tool_ent:destroy()
	end
	
	self.active_tools[tool_info.name] = nil
	self:removeCell(tool_info)
end

function InventoryBase:lockTool(tool_info)
	self.locked_tools[tool_info.name] = true
	
	if not self:isFlyingTool(tool_info) then
		tool_info.entities.tool_ent:setVisible(false)
		tool_info.entities.tool_shadow_ent:setVisible(true)
		if tool_info.additional_info.collection_text then
			tool_info.additional_info.collection_text:setVisible(false)
		end
	end
end

function InventoryBase:unlockTool(tool_info)
	self.locked_tools[tool_info.name] = nil
	
	if not self:isFlyingTool(tool_info) then
		tool_info.entities.tool_ent:setVisible(true)
		tool_info.entities.tool_shadow_ent:setVisible(false)
		if tool_info.additional_info.collection_text then
			tool_info.additional_info.collection_text:setVisible(true)
		end
	end
end

function InventoryBase:moveToolToFreeSlot(tool_info, time)
	local cell_info = self:getCellInfoByToolInfo(tool_info)
	
	-- Если для тулзы не найдена ячейка
	if not cell_info then
		-- Проверяем, входит ли тулза в коллекцию
		local collection = tool_info.additional_info.collection
		if collection then
			for _, t_name in ipairs( collection:get_tools() ) do
				if tool_info.name ~= t_name and self.active_tools[t_name] then
					cell_info = self:getCellInfoByToolInfo(self.active_tools[t_name])
					break
				end
			end
			
			-- Если попали сюда, то это первый тулз из коллекции
			tool_info.additional_info.collection_owner = true
		end
	
		--
		if not cell_info then
			cell_info = self:createCell(tool_info)
		end
		
		tool_info.entities.tool_shadow_ent:attachTo(cell_info.cell_entity)
		tool_info.entities.tool_shadow_ent:setOrder(1)
		tool_info.entities.tool_shadow_ent:setXY(0, 0)
	end
	
	local cell_entity = cell_info.cell_entity
	tool_info.entities.tool_ent:setOrder(2)
	
	self.flying_tools[tool_info.name] = true
	
	if time == 0 then
		tool_info.entities.tool_ent:setScrXY(cell_entity:getScrX(),cell_entity:getScrY())
		self:onToolMoveToInventoryFinish(tool_info, cell_entity)
	else
		local eff = tool_info.entities.tool_ent:MoveTo(cell_entity:getScrX(), cell_entity:getScrY(), time, nil, true)
		self:lockTool(tool_info)
		self.timer:add( function()
							self:onToolMoveToInventoryFinish(tool_info, cell_entity)
							self:unlockTool(tool_info)
						end, time)
	end
end

function InventoryBase:onToolMoveToInventoryFinish(tool_info, target_cell_entity)
	self.flying_tools[tool_info.name] = nil
	
	local tool_ent = tool_info.entities.tool_ent
	local cell = self:getCellInfoByToolInfo(tool_info)
	
	-- Устанавливаем обработчик для взятия тулзы
	local function on_click_cell_check(cell_entity, info)
		if cell_entity and not self:isSliding() then
			if self.tool_in_hand then
				self:dropTool()
			end
			
			local cell_info = self:getCellInfoByEntity(cell_entity)
			local new_tool = cell_info.tool_info
			
			if not self:isLockedTool(new_tool) and
			   not self:isFlyingTool(new_tool)
			then
				if cell_info.show then
					self:takeTool(new_tool)
				end
			end
			
			self.hintText:setVisible(false)
	
			if not self:getPlayField():getMouseBlocker():isHold() then
				self:getPlayField():hideBlackBar()
			end
		end
	end
	
	-- Обработчик получения фокуса ячейкой
	local function on_cell_focus_in(cell_entity)
		if not self.tool_in_hand then
			self.hintedTool = self:getCellInfoByEntity(cell_entity).tool_info
			
			self.hintText:setVisible(true)
			self.hintText:setText(StringManager:getString(self.hintedTool.texts.hint_text))
		end
	end
	
	-- Обработчик потери фокуса ячейкой
	local function on_cell_focus_out(cell)
		self.hintedTool = nil
		self.hintText:setVisible(false)
	end
	
	tool_ent:destroyEffectors()
	tool_ent:attachTo(target_cell_entity)
	tool_ent:setOrder(2)
	tool_ent:setXY(0, 0)
	
	if cell.tool_info.name == tool_info.name then
		target_cell_entity:setCursorOverName('CursorActive')
		if not cell.actions then
			cell.actions = {}
			cell.actions['onClickBegin'] = target_cell_entity.onClickBegin:add(on_click_cell_check)
			cell.actions['onFocusIn'] = target_cell_entity.onFocusIn:add(on_cell_focus_in)
			cell.actions['onFocusOut'] = target_cell_entity.onFocusOut:add(on_cell_focus_out)
			target_cell_entity:setSendEventsToParent(false)
		end
		
		if self:isLockedTool(tool_info) then
			tool_ent:setVisible(false)
			tool_info.entities.tool_shadow_ent:setVisible(true)
		else
			tool_ent:setVisible(true)
			tool_info.entities.tool_shadow_ent:setVisible(false)
		end
	else
		-- Если попали сюда, значить этот предмет летит в коллекцию
		tool_ent:setVisible(false)
		tool_info.entities.tool_shadow_ent:setVisible(false)
	end
	
	self:updateCollectionText(cell)
end

function InventoryBase:isActiveTool(tool_info)
	assert(tool_info)
	if self.active_tools[tool_info.name] then
		return true
	end
	
	return false
end

function InventoryBase:isFlyingTool(tool_info)
	assert(tool_info)
	return (self.flying_tools[tool_info.name] == true)
end

function InventoryBase:isLockedTool(tool_info)
	assert(tool_info)
	return (self.locked_tools[tool_info.name] == true)
end

function InventoryBase:getToolInHand()
	return self.tool_in_hand
end

function InventoryBase:getToolInfoByName(name)
	assert(name)
	return self.active_tools[name]
end

function InventoryBase:getCellInfoByEntity(entity)
	assert(entity)
	for i, info in pairs(self.cells) do
		if info.cell_entity:getName() == entity:getName() then
			return info
		end
	end
	
	return nil
end

function InventoryBase:getCellInfoByToolInfo(tool_info)
	assert(tool_info)
	for index, cell_info in pairs(self.cells) do
		if cell_info.tool_info then
			-- 
			if cell_info.tool_info.name == tool_info.name then
				return cell_info, index
				
			-- Поиск тулзы в коллекции
			elseif cell_info.tool_info.additional_info.collection then
				for i, t_name in pairs(cell_info.tool_info.additional_info.collection:get_tools()) do
					if t_name == tool_info.name and self:isActiveTool(cell_info.tool_info) then
						return cell_info, index
					end
				end
			end
		end
	end
	
	return nil, nil
end

function InventoryBase:setIndex(index)
	if #self.cells < self.settings.VISIBLE_CELLS_COUNT then
		index = 0
	elseif index > (#self.cells - self.settings.VISIBLE_CELLS_COUNT) then
		index = #self.cells - self.settings.VISIBLE_CELLS_COUNT
	end
	
	self.anchor_col = -index
	self.anchor:setX( self.settings.START_ANCHOR_POS.x + (self.anchor_col * self.settings.CELLS_WIDTH) )
	
	self:checkCellsOpacity(0)
	self:checkSlideBtnsEnable()
end

function InventoryBase:getIndex(index)
	return -self.anchor_col
end

function InventoryBase:getVisibleCellsCount()
	return self.settings.VISIBLE_CELLS_COUNT
end

function InventoryBase:lockInventory()
	AddUniqueGameState(INVENTORY_LOCK, 'true')
end

function InventoryBase:unlockInventory()
	if HasGameState(INVENTORY_LOCK) then
		RemoveGameState(INVENTORY_LOCK)
	end
end

function InventoryBase:slideUp(speed)
	local obj = self.root

	speed = speed and speed or 0
	if self.SlideEff ~= nil then
		self.SlideEff:destroy()
		self.SlideEff = nil
	end
	if self.SlideTimer then
		self.SlideTimer:kill()
	end
	
	if speed == 0. then
		obj:setXY(self.settings.ROOT_SHOW_POSITION.x, self.settings.ROOT_SHOW_POSITION.y)
		self.btn_left:setOpacity(1)
		self.btn_right:setOpacity(1)
		return
	end
	
	local dy = self.settings.ROOT_SHOW_POSITION.y - obj:getY()
	local dir = (dy ~= 0) and dy/math.abs(dy) or 0
	local momentumY = 3
	
	local SpeedK = (obj:getY() - self.settings.ROOT_SHOW_POSITION.y) / (self.settings.ROOT_SLIDE_POSITION.y - self.settings.ROOT_SHOW_POSITION.y)
	local time = speed * SpeedK
	
	self.SlideEff = CreateAnimationEffector()
	self.SlideEff:attachTo(obj)
	self.SlideEff:setKey(_TRACK_Y, 0,         obj:getY())
	self.SlideEff:setKey(_TRACK_Y, time*0.7,  self.settings.ROOT_SHOW_POSITION.y + momentumY*dir)
	self.SlideEff:setKey(_TRACK_Y, time,      self.settings.ROOT_SHOW_POSITION.y)
	self.SlideEff:play()

	if time > 0. then
		local delay = 0.4
		-- Вложенные таймеры, чтобы избежать сохранения 2-х action в self.SlideTimer
		self.SlideTimer = self.timer:add(function()
											playCommonSound('InventoryOpening')
											self.timer:add(function()
																self.btn_left:setOpacity(1)
																self.btn_right:setOpacity(1)
											end, delay)
									 end, time - delay)
	end
end

function InventoryBase:slideDown(speed)
	if HasGameState(INVENTORY_LOCK) or (self.tool_in_hand and getToolComponent and getToolComponent(self.tool_in_hand.name)) then --not hide inventory if composit tool in hand (you can take function getToolComponent from Graveyard4 Scripts/Tools/ComponentToolsList.lua)
		return
	end

	speed = speed and speed or 0.
	if self.SlideEff ~= nil then
		self.SlideEff:destroy()
		self.SlideEff = nil
	end
	if self.SlideTimer then
		self.SlideTimer:kill()
	end
	
	if speed == 0 then
		self.root:setXY(self.settings.ROOT_SLIDE_POSITION.x, self.settings.ROOT_SLIDE_POSITION.y)
		self.btn_left:setOpacity(0)
		self.btn_right:setOpacity(0)
		return
	end
	
	local SpeedK = (self.settings.ROOT_SLIDE_POSITION.y - self.root:getY()) / (self.settings.ROOT_SLIDE_POSITION.y - self.settings.ROOT_SHOW_POSITION.y)
	local time = speed * SpeedK
	
	self.SlideEff = self.root:MoveTo(self.settings.ROOT_SLIDE_POSITION.x, self.settings.ROOT_SLIDE_POSITION.y, time)
	self.SlideEff:setDestroysOnEnd(false)
	
	self.btn_left:setOpacity(0)
	self.btn_right:setOpacity(0)
	
	-- FIXME Оживить звук как инвентарь прячется
	--[[
	if time > 0. then self.SlideTimer = self.timer:Add(function()  playCommonSound('Inventory_hide') end, time) end
	]]
end

function InventoryBase:slideInventoryAt(deltaCol, isAuxAnchor, restoring)
	local posrfix = (isAuxAnchor) and 'Aux' or ""
	
	local slide_time = self.settings.SLIDE_TIME
	if restoring then
		slide_time = 0 
	end
	
	self.slidingAnchor = self['anchor'..posrfix]
	
	local effName      = 'slideEff'..posrfix
	local actionName   = 'slideAction'..posrfix
	local colName      = 'anchor_col'..posrfix
	
	if self[effName] then
		self[effName]:destroy()
		self[effName] = nil
		
		self[actionName]:kill()
	end
	
	self[colName] = self[colName] + deltaCol
	if #self.cells == self.settings.VISIBLE_CELLS_COUNT then
		SetGameState(INVENTORY_SHIFT, '0')
	else
		SetGameState(INVENTORY_SHIFT, tostring(self[colName]))
	end
	
	local newX = self.settings.START_ANCHOR_POS.x + (self[colName] * self.settings.CELLS_WIDTH)
	local dx   = newX - self.slidingAnchor:getX()
	local dir  = (dx ~= 0) and dx/math.abs(dx) or 0
	local momentumX = 5

	local eff = CreateAnimationEffector()
	eff:attachTo( self.slidingAnchor )
	eff:setKey(_TRACK_X, 0,              self.slidingAnchor:getX())
	eff:setKey(_TRACK_X, slide_time*0.5, newX)
	eff:setKey(_TRACK_X, slide_time*0.7, newX + momentumX*dir)
	eff:setKey(_TRACK_X, slide_time,     newX)
	eff:play()
	
	self[effName] = eff
	self[actionName] = self.timer:add(  function()
											self[effName]:destroy()
											self[effName] = nil
														
											-- Закоментировано в оригинальном коде
											--self.slidingAnchor:SetX(newX)
											self.slidingAnchor = nil
											self:dettachCellsFromAuxAnchor()
										end, slide_time)
	
	self:checkCellsOpacity(slide_time * 0.5)
	self:checkSlideBtnsEnable()
end

function InventoryBase:slideInventoryTo(col)
	local deltaCol = -(self.anchor_col + col)
	
	if (deltaCol ~= 0) then
		self:slideInventoryAt(deltaCol)
	end
end

function InventoryBase:checkSlideBtnsEnable()
	local cnt = #self.cells
	
	if self.cells[cnt].notInInventoryYet then
		cnt = cnt - 1
	end
	
	if cnt <= self.settings.VISIBLE_CELLS_COUNT or (self.anchor_col ) >= 0 then
		self.btn_left:setPressed(false)
		self.btn_left:setActive(false)
	else
		self.btn_left:setActive(true)
	end
	
	if cnt <= self.settings.VISIBLE_CELLS_COUNT or (self.anchor_col + cnt) <= self.settings.VISIBLE_CELLS_COUNT then
		self.btn_right:setPressed(false)
		self.btn_right:setActive(false)
	else
		self.btn_right:setActive(true)
	end
end

function InventoryBase:isSliding()
	return (self.slidingAnchor ~= nil)
end

function InventoryBase:onToolFound(tool_info)
	self.active_tools[tool_info.name].notInInventoryYet = nil    
	tool_info.entities.tool_ent:setVisible(true)
	
	self:checkSlideBtnsEnable()
end

function InventoryBase:onUpdate(dt)
	self.timer:update(dt)
	
	if self.tool_in_hand then
		local mouse_pos = GetCursorEntity():getScrPosition()
		self.tool_in_hand.entities.tool_ent:setScrXY(mouse_pos.x, mouse_pos.y)
		self.tool_in_hand.entities.tool_ent:setOrder(100) -- Можно уменьшить, если что
	end
	
	self:updateHintText()
end

function InventoryBase:onFocusInInventoryPanel()
	if self.SlideTimer then
		self.SlideTimer:kill()
	end
	self:slideUp(self.settings.SLIDE_INV_TIME)
end

function InventoryBase:onFocusOutInventoryPanel()
	local function Timer_SlideDown()
		self:slideDown(self.settings.SLIDE_INV_TIME)
	end
	self.SlideTimer = self.timer:add(Timer_SlideDown, self.settings.SLIDE_INV_DELAY)
end

function InventoryBase:updateHintText()
	local cell_info
	if self.hintedTool then
		cell_info = self:getCellInfoByToolInfo(self.hintedTool)
	end
	
	if  cell_info
		and cell_info.show
		and cell_info.tool_info
		and not self.tool_in_hand
		and not self:isFlyingTool(cell_info.tool_info)
		and not self:isLockedTool(cell_info.tool_info)
	then
		local tool_name = cell_info.tool_info.name
		local mouse = GetCursorEntity():getScrPosition()
		
		self.hintText:setScrXY(mouse.x + self.settings.TOOL_NAME_TEXT_OFFSET_X, mouse.y + self.settings.TOOL_NAME_TEXT_OFFSET_Y)
	end
end

function InventoryBase:registerAllTools()
	local auxRegisterTool = function(toolName, inv_path, window_path, add_tmp_mark)
		assert(toolName and "All parameters haven't been specified!")
		
		local additional_info = {}
		additional_info.global_targets = getToolGlobalTargets(toolName)
		additional_info.collection = getCollectionTools(toolName)

		local function create_tool()
			local entity = CreateSpriteEntity(inv_path)
			entity:setName(toolName..TOOL_INVENTORY_SUFFIX)
			entity:setXY(512, 384)
			entity:setOrder(1)
			entity:setRespondable(false)
			
			if decorateToolSprite then
				decorateToolSprite(toolName, entity, false)
			end
				
			if add_tmp_mark then
				local tmp_mark = CreateSpriteEntity("")
				tmp_mark:setName(toolName..TOOL_INVENTORY_SUFFIX.."_tmp_mark")
				tmp_mark:setAnimationSize(10,10)
				WriteWarning("Temporaty tool mark created for " .. toolName)
				tmp_mark:setXY(-32, -32)
				tmp_mark:setColor(1,0,0,1)
				tmp_mark:setRespondable(false)
				tmp_mark:attachTo(entity)
			end
			
			return entity
		end
		
		local function create_tool_shadow()
			local entity = CreateSpriteEntity(inv_path)
			entity:setName(toolName..TOOL_SHADOW_SUFFIX)
			entity:setXY(512, 384)
			entity:setOrder(1)
			entity:setColor(Colorf(1, 1, 1, 0.4))
			entity:setRespondable(false)
			return entity
		end
		
		local function create_info_icon()
			local entity = CreateSpriteEntity(window_path)
			entity:setName(toolName..TOOL_WINDOW_SUFFIX)
			entity:setXY(512, 384)
			entity:setOrder(1)
			entity:setRespondable(false)
			
			if decorateToolSprite then
				decorateToolSprite(toolName, entity, true)
			end
			
			return entity
		end
		
		local create_entities_functions = {}
		create_entities_functions.create_tool = create_tool
		create_entities_functions.create_tool_shadow = create_tool_shadow
		create_entities_functions.create_info_icon = create_info_icon
		
		self:registerTool(toolName, create_entities_functions, additional_info)
	end

	local function parseToolsFolders(toolFolder)
		local toolInfoVec = getResourceFolderSubfolders(toolFolder)
		for i = 0, toolInfoVec:size() - 1 do
			local path = toolInfoVec:get(i)
			local name_tool = ExtractFileName(path)
			local inv_path
			local window_path
			local add_tmp_mark
			
			local iconsInfoVec = getResourceFolderFiles(path)
			
			for j = 0, iconsInfoVec:size() - 1 do
				local icon = iconsInfoVec:get(j)
				if string.find(icon, 'inv') then
					inv_path = icon
				elseif string.find(icon, 'wnd') then
					window_path = icon
				elseif string.find(icon, 'tmp') then
					add_tmp_mark = true
				end
			end
			
			auxRegisterTool(name_tool, inv_path, window_path, add_tmp_mark)
		end
	end

	local level_name = current_scene.name
	local toolsFolder = nil
	if IsLevelCollectorsEdition(level_name) then
		toolsFolder = TOOL_FOLDER_EXTRA
	else
		toolsFolder = TOOL_FOLDER
	end

	parseToolsFolders(toolsFolder)

	-- Добавляем в команду консоли поика тулзов.
	if isBuildForCustomerMode() == false then
		local command = Console:getCommand('findTool')
		if command then
			for tool_name, _ in pairs(self.tools) do
				if not IsToolFound(tool_name) then
					command:addPossibleParam(tool_name)
				end
			end
		end
	end
end

function InventoryBase:registerTool(tool_name, create_entities_functions, additional_info)
	-- Инициализация структуры
	local tool_info = {}
	tool_info.name = tool_name
	tool_info.create_entities_functions = table.copy(create_entities_functions)
	tool_info.additional_info = table.copy(additional_info)
	tool_info.texts = {}
	tool_info.texts.info_text = 'TOOL_INFO_' .. tool_name:upper()
	tool_info.texts.hint_text = 'TOOL_HINT_' .. tool_name:upper()
	tool_info.entities = {}

	-- Сохранение в общую таблицу
	self.tools[tool_name] = tool_info

	-- Добавляем ранее найденые тулзы в инвентарь
	if IsToolFound(tool_name) and not IsToolApplied(tool_name) then
		assert(tool_info.create_entities_functions.create_tool ~= nil)
		
		-- Создаём иконки
		tool_info.entities.tool_ent = tool_info.create_entities_functions.create_tool()
		tool_info.entities.tool_shadow_ent = tool_info.create_entities_functions.create_tool_shadow()

		if HasGameState(TOOL_INFO_SHOWN, name) then
			self:addTool(tool_info)
		end
	end
end

function InventoryBase:getToolInfoCollectionOwner(tool_info)
	for i, v in	pairs(self.tools) do
		if v.additional_info.collection and v.additional_info.collection:is_exist_tool(tool_info.name) then
			if v.additional_info.collection_owner then
				return v
			end
		end
	end

	return nil
end

function InventoryBase:updateCollectionText(cell_info)
	local tool_info = self:getToolInfoCollectionOwner(cell_info.tool_info)
	local collection
	if tool_info then
		collection = tool_info.additional_info.collection
	end
	
	if collection then
		local size_tools_found  = collection:get_size_tools_found()
		local size_tools        = collection:get_size_tools()
		local text              = tostring(size_tools_found)..'/'..tostring(size_tools)
		local x                 = self.settings.TOOL_COUNTER_OFFSET.x
		local y                 = self.settings.TOOL_COUNTER_OFFSET.y
		
		if not tool_info.additional_info.collection_text then
			local collection_text = CreateTextEntity()
			collection_text:setFont			( self.settings.TEXT_FONT )
			collection_text:attachTo		( cell_info.cell_entity )
			collection_text:setXY			( x, y )
			collection_text:setScale		( self.settings.TOOL_COUNTER_FONT_SCALE )
			collection_text:setOrder		( 3 ) -- устанавливаем выше тени и тулзы
			collection_text:setRespondable	( false )
			--collection_text:hide()
		
			tool_info.additional_info.collection_text = collection_text
		end
		
		tool_info.additional_info.collection_text:setText(text)
	end
end

function InventoryBase:clearToolsEvents()
	for i, v in pairs(self.tools) do
		v.on_apply = nil
		v.on_get = nil
		v.on_drop = nil
		v.on_find = nil
		v.on_tool_window_close = nil
	end
end
-------------------------------------------------------------------------------


--------------------------------------------------------------------------------
--                      GENERATED CONTENT. DO NOT EDIT.                       --
--------------------------------------------------------------------------------

function InventoryBase:CreateLoaderTable()
	local objects =
	{
	}
	return objects
end
