voxel_radius = {
	x = minetest.settings:get("voxel_obs_rx"),
	y = minetest.settings:get("voxel_obs_ry"),
	z = minetest.settings:get("voxel_obs_rz")
}

-- names of the items included in the initial inventory
init_tools = { "mcl_tools:axe_stone", "mcl_torches:torch 256" }

timeofday_step = 1 / 5000 -- day/night cycle lasts 5000 steps
timeofday = 0.5 -- start episode at midday

-- executed when the player joins the game
minetest.register_on_joinplayer(function(player, _last_login)
	minetest.set_timeofday(timeofday)

	-- disable HUD elements
	player:hud_set_flags({
		crosshair = false,
		basic_debug = false,
		chat = false,
	})

	-- setup initial inventory
	local inv = player:get_inventory()
	for i=1, #init_tools do
		inv:add_item("main", init_tools[i])
	end

	-- set player's initial position
	player:set_pos({x = 120, z = 92, y = 16.5 })
end)

-- turn on the termination flag if the agent dies
minetest.register_on_dieplayer(function(ObjectRef, reason)
	set_termination()
end)

-- make game's time match with learning timesteps
minetest.register_globalstep(function(dtime)
	if timeofday > 1.0 then
		timeofday = 0.0
	end
	minetest.set_timeofday(timeofday)
	timeofday = timeofday + timeofday_step

	local player = minetest.get_connected_players()[1]

	-- if the player is not connected end here
	if player == nil then
		return nil
	end

	-- if the player is connected:
	local player_pos = player:get_pos()
	if minetest.settings:get("voxel_obs") then
		local voxel_data, voxel_light_data, voxel_param2_data = voxel_api:get_voxel_data(player_pos, voxel_radius)
		set_voxel_data(voxel_data)
		set_voxel_light_data(voxel_light_data)
		set_voxel_param2_data(voxel_param2_data)
	end
end)

--
-- Tools
-- ~~~~~

stages = {
	-- Inital stage
	{ name = "init" },
	-- Wood stage
	{
		name = "wood", -- name of the stage
		req_name = "tree", -- substring included in the target node's name
		req_num = 2, -- number of resources (req_name) required to jump to the next stage
		current = 0, -- number of (target) resources currently obtained
		provides = { "mcl_tools:pick_wood", "mcl_tools:sword_wood" }, -- the tools provided upon completion
		reward = 128.0, -- the reward of completing the stage
	},
	-- Stone stage
	{
		name = "stone",
		req_name = "stone",
		req_num = 3,
		current = 0,
		provides = { "mcl_tools:pick_stone", "mcl_tools:sword_stone" },
		reward = 256.0,
	},
	-- Stone stage
	{
		name = "iron",
		req_name = "iron",
		req_num = 3,
		current = 0,
		provides = { "mcl_tools:pick_iron", "mcl_tools:sword_iron", "mcl_tools:axe_iron" },
		reward = 1024.0,
	},
	{
		name = "diamond",
		req_name = "diamond",
		req_num = 1,
		current = 0,
		provides = { "mcl_tools:pick_diamond", "mcl_tools:sword_diamond", "mcl_tools:axe_diamond" },
		reward = 2048.0,
	},
	{ name = "end" }
}

curr_stage = 1 -- index of the current stage

minetest.register_on_dignode(function(pos, node)
	-- table of the next stage
	local snext = stages[curr_stage+1]

	-- there's nothing to do if we reached the last stage
	if snext.name == "end" then
		return
	end

	-- check if the dug node contains the `req_name` substring
	if string.find(node["name"], snext.req_name) then
		snext.current = snext.current + 1
	end

	-- check if we've enough resources to jump to the next stage
	if snext.current >= snext.req_num then
		print(string.format("[STAGE] Stage '%s' starts", snext.name), os.date())

		-- provide one timestep reward
		set_reward_once(snext.reward, 0.0)

		-- add new tools to the inventory (removing the current ones first)
		for _, player in pairs(minetest.get_connected_players()) do
			local inv = player:get_inventory()
			inv:set_list("main", {}) -- empty inventory
			for i=1, #init_tools do -- reset initial inventory (in the same slots)
				inv:add_item("main", init_tools[i])
			end
			for i = 1, #snext.provides do -- add the unlocked tools
				inv:add_item("main", snext.provides[i])
			end
		end

		-- Move to the next stage
		curr_stage = curr_stage + 1
	end
end)


--
-- Hunt and Defend
-- ~~~~~~~~~~~~~~~

for name, _ in pairs(mcl_mobs.spawning_mobs) do -- for all mobs that spawn
	-- access its definition
	local mob_def = minetest.registered_entities[name]
	local mob_type = mob_def.type
	local old_on_punch = mob_def.on_punch
	mob_def.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
		local damage = tool_capabilities.damage_groups
			and tool_capabilities.damage_groups.fleshy or 1
		-- provide a different reward for each type of punched mob
		if mob_type == "monster" then
			set_reward_once(damage, 0.0)
		elseif mob_type == "animal" then
			set_reward_once(damage*0.5, 0.0)
		elseif mob_type == "npc" then
			set_reward_once(-10.0, 0.0)
		end
		-- Call the original on_punch function
		if old_on_punch ~= nil then
			old_on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
		end
	end
end
