-- This mod disallows changing blocks in certain game modes.
-- Normally, out-of-bounds blocks cannot be changed.
-- Developer Mode bypasses this restriction. This can also be
-- bypassed with the protection_bypass privilege.
-- This also disallows taking blocks in the game when the inventory is full.

-- How many seconds to flash the hotbar if the inventory was full
local HOTBAR_FLASH_TIME = 0.5

-- Checks if the player would have room for the given node
-- in inventory and returns true. Or false if inventory has
-- no room left. Exception:
-- When player wields a non-digging tool, always returns
-- true.
local function has_room_for_node_drops(player, node)
	local tool = player:get_wielded_item()
	local tooldef = tool:get_definition()
	-- This is not a digging tool as it would do
	-- something else on dig
	if tooldef.on_use then
		return true
	end
	local inv = player:get_inventory()
	local def = minetest.registered_nodes[node.name]
	-- This assumes drops aren't random (which wouldn't
	-- make sense in this game anyway)
	local drops = minetest.get_node_drops(node, tool)
	for d=1, #drops do
		local drop = drops[d]
		if not inv:room_for_item("main", drop) then
			return false
		end
	end
	return true
end

local old_is_protected = minetest.is_protected

minetest.is_protected = function(pos, name)
	local state = lzr_gamestate.get_state()
	-- Can't dig if inventory is full! (unless in editor or dev mode)
	if state ~= lzr_gamestate.EDITOR and state ~= lzr_gamestate.DEV then
		local player = minetest.get_player_by_name(name)
		local node = minetest.get_node(pos)
		if not has_room_for_node_drops(player, node) then
			return true
		end
	end

	-- The 'protection_bypass' priv is limited and is basically only
	-- useful to bypass protection in the MENU state.
	-- In the level and editor other game states, blocks are hard-protected
	-- for stability. Also, so that '/grantme all' annoyingly doesn't allow
	-- you to destroy the level border in the editor.
	-- The official way for the player to edit everything is by
	-- entering Developer Mode via '/devmode'.

	local protection_bypass = false
	if minetest.check_player_privs(name, { protection_bypass = true }) then
		protection_bypass = true
	end
	-- Always protected when level is complete
	if state == lzr_gamestate.LEVEL_COMPLETE then
		return true
	-- Protect out-of-bounds blocks in a level or editor
	elseif state == lzr_gamestate.LEVEL or state == lzr_gamestate.LEVEL_TEST or state == lzr_gamestate.EDITOR then
		return not lzr_world.is_in_level_bounds(pos)
	-- Default protection in menu (can bypass)
	elseif state == lzr_gamestate.MENU then
		return (not protection_bypass) or old_is_protected(pos, name)
	-- Default protection in dev mode
	elseif state == lzr_gamestate.DEV then
		return old_is_protected(pos, name)
	end
	return old_is_protected(pos, name)
end

local flash_jobs = {}

minetest.register_on_protection_violation(function(pos, name)
	local state = lzr_gamestate.get_state()
	local inventory_full = false
	local player = minetest.get_player_by_name(name)
	if state ~= lzr_gamestate.EDITOR and state ~= lzr_gamestate.DEV then
		local node = minetest.get_node(pos)
		if not has_room_for_node_drops(player, node) then
			inventory_full = true

			-- If there is already a job running, cancel it
			if flash_jobs[name] then
				flash_jobs[name]:cancel()
				flash_jobs[name] = nil
			end
			-- Play warning sound and highlight the hotbar
			minetest.sound_play({name = "lzr_protection_inventory_full", gain = 0.3}, {to_player=name}, true)
			lzr_gui.set_hotbar_highlight(player, true)

			-- Disable flashing after a brief moment
			local job = minetest.after(HOTBAR_FLASH_TIME, function(player)
				if player and player:is_player() then
					lzr_gui.set_hotbar_highlight(player, false)
				end
			end, player)
			-- Remember this 'after' job
			flash_jobs[name] = job
		end
	end
	if not inventory_full and state ~= lzr_gamestate.LEVEL_COMPLETE then
		minetest.sound_play({name = "lzr_protection_inventory_full", gain = 0.3}, {to_player=name}, true)
		minetest.add_particle({
			pos = pos,
			texture = "lzr_protection_forbidden.png",
			glow = minetest.LIGHT_MAX,
			size = 8,
			expirationtime = 1.0,
		})
	end
end)

minetest.register_on_leaveplayer(function(player)
	local pname = player:get_player_name()
	if flash_jobs[pname] then
		flash_jobs[pname]:cancel()
		flash_jobs[pname] = nil
	end
end)
