-------------------------------------------------------------------------------
-- Mob Framework Mod by Sapier
--
-- You may copy, use, modify or do nearly anything except removing this
-- copyright notice.
-- And of course you are NOT allow to pretend you have written it.
--
--! @file weapons.lua
--! @brief weapon related functions
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup weapons Weapons
--! @brief weapon entitys predefined by mob framework (can be extended by mod)
--
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(weapons_spacer == nil)
local weapons_spacer = {} --unused to fix lua doxygen bug only

-------------------------------------------------------------------------------
-- name: mobf_init_weapons = function(self, dtime)
--
--! @brief initialize weapons handled by mobf mod
--
-------------------------------------------------------------------------------
function mobf_init_weapons()
	minetest.register_entity(":mobf:fireball_entity", MOBF_FIREBALL_ENTITY)
	minetest.register_entity(":mobf:plasmaball_entity", MOBF_PLASMABALL_ENTITY)
	minetest.register_entity(":mobf:arrow_entity", MOBF_ARROW_ENTITY)
end

-------------------------------------------------------------------------------
-- name: mobf_do_area_damage(pos,immune,damage_groups,range)
--
--! @brief damage all objects within a certain range
--
--! @param pos cennter of damage area
--! @param immune object immune to damage
--! @param damage_groups list of damage groups to do damage to
--! @param range range around pos
-------------------------------------------------------------------------------
function mobf_do_area_damage(pos,immune,damage_groups,range)
	--damage objects within inner blast radius
	mobf_assert_backtrace(type(range) ~= "table")

	local objs = minetest.get_objects_inside_radius(pos, range)
	for k, obj in pairs(objs) do

		--don't do damage to issuer
		if obj ~= immune and obj ~= nil then

			--TODO as long as minetest still crashes without puncher use this workaround

			local worst_damage = 0
			if type(damage_groups) == "table" then
				for k,v in pairs(damage_groups) do
					if v > worst_damage then
						worst_damage = v
					end
				end
			elseif type(damage_groups) == "number" then
				worst_damage =  damage_groups
			else
				mobf_assert_backtrace("invalid damage_groups" == "selected")
			end


			local current_hp = obj:get_hp()
			obj:set_hp(current_hp - worst_damage)

			--punch
			--obj:punch(nil, 1.0, {
			--	full_punch_interval=1.0,
			--	damage_groups = damage_groups,
			--}, nil)
		end
	end
end


-------------------------------------------------------------------------------
-- name: mobf_do_node_damage(pos,immune_list,range,chance)
--
--! @brief damage all nodes within a certain range
--
--! @param pos center of area
--! @param immune_list list of nodes immune to damage
--! @param range range to do damage
--! @param chance chance damage is done to a node
-------------------------------------------------------------------------------
function mobf_do_node_damage(pos,immune_list,range,chance)
	--do node damage
	for i=pos.x-range, pos.x+range, 1 do
		for j=pos.y-range, pos.y+range, 1 do
			for k=pos.z-range,pos.z+range,1 do
				--TODO create a little bit more sophisticated blast resistance
				if math.random() < chance then
					local toremove = minetest.get_node({x=i,y=j,z=k})

					if toremove ~= nil then
						local immune = false

						if immune_list ~= nil then
							for i,v in ipairs(immune_list) do
								if (torremove.name == v) then
									immune = true
								end
							end
						end


						if immune ~= true then
							minetest.remove_node({x=i,y=j,z=k})
						end
					end
				end
			end
		end
	end
end

mobf_assert_backtrace(MOBF_FIREBALL_ENTITY == nil)
--! @class MOBF_FIREBALL_ENTITY
--! @ingroup weapons
--! @brief a fireball weapon entity
MOBF_FIREBALL_ENTITY = {
	physical = false,
	textures = {"animals_fireball.png"},
	collisionbox = {0,0,0,0,0,0},

	damage_range = 4,
	velocity = 3,
	gravity = -0.01,

	damage_outer = {
					fleshy=4,
					},
	damage_inner = {
					fleshy=8,
					},

	owner = 0,
	lifetime = 30,
	created = -1,
}


-------------------------------------------------------------------------------
-- name: MOBF_FIREBALL_ENTITY.on_activate = function(self, staticdata)
--
--! @brief onactivate callback for fireball
--! @memberof MOBF_FIREBALL_ENTITY
--! @private
--
--! @param self fireball itself
--! @param staticdata
-------------------------------------------------------------------------------
function MOBF_FIREBALL_ENTITY.on_activate(self,staticdata)
	self.created = mobf_get_current_time()
end

-------------------------------------------------------------------------------
-- name: MOBF_FIREBALL_ENTITY.surfacefire = function(self, staticdata)
--
--! @brief place fire on surfaces around pos
--! @memberof MOBF_FIREBALL_ENTITY
--! @private
--
--! @param pos position to place fire around
--! @param range square around pos to set on fire
-------------------------------------------------------------------------------
function MOBF_FIREBALL_ENTITY.surfacefire(pos,range)

	if mobf_rtd.fire_enabled then
		--start fire on any surface within inner damage range
		for i=pos.x-range/2, pos.x+range/2, 1 do
		for j=pos.y-range/2, pos.y+range/2, 1 do
		for k=pos.z-range/2, pos.z+range/2, 1 do

			local current = minetest.get_node({x=i,y=j,z=k})
			local ontop  = minetest.get_node({x=i,y=j+1,z=k})

			--print("put fire? " .. printpos({x=i,y=j,z=k}) .. " " .. current.name .. " " ..ontop.name)

			if (current.name ~= "air") and
				(current.name ~= "fire:basic_flame") and
				(ontop.name == "air") then
				minetest.set_node({x=i,y=j+1,z=k}, {name="fire:basic_flame"})
			end

		end
		end
		end
	else
		minetest.log(LOGLEVEL_ERROR,"MOBF: A fireball without fire mod??!? You're kidding!!")
	end
end

-------------------------------------------------------------------------------
-- name: MOBF_FIREBALL_ENTITY.on_step = function(self, dtime)
--
--! @brief onstep callback for fireball
--! @memberof MOBF_FIREBALL_ENTITY
--! @private
--
--! @param self fireball itself
--! @param dtime time since last callback
-------------------------------------------------------------------------------
function MOBF_FIREBALL_ENTITY.on_step(self, dtime)
	local pos = self.object:getpos()
	local node = minetest.get_node(pos)


	--detect hit
	local objs=minetest.get_objects_inside_radius({x=pos.x,y=pos.y,z=pos.z}, 1)

	local hit = false

	for k, obj in pairs(objs) do
		if obj:get_entity_name() ~= "mobf:fireball_entity" and
			obj ~= self.owner then
			hit=true
		end
	end


	if hit then
		--damage objects within inner blast radius
		mobf_do_area_damage(pos,self.owner,self.damage_outer,self.damage_range/4)

		--damage all objects within blast radius
		mobf_do_area_damage(pos,self.owner,self.damage_inner,self.damage_range/2)

		MOBF_FIREBALL_ENTITY.surfacefire(pos,self.damage_range)

		self.object:remove()
	end

	-- vanish when hitting a node
	if node.name ~= "air" then
		MOBF_FIREBALL_ENTITY.surfacefire(pos,self.damage_range)
		self.object:remove()
	end

	--remove after lifetime has passed
	if self.created > 0 and
		self.created + self.lifetime < mobf_get_current_time() then
		self.object:remove()
	end
end


mobf_assert_backtrace(MOBF_PLASMABALL_ENTITY == nil)
--! @class MOBF_PLASMABALL_ENTITY
--! @ingroup weapons
--! @brief a plasmaball weapon entity
MOBF_PLASMABALL_ENTITY = {
	physical = false,
	textures = {"animals_plasmaball.png"},
	lastpos={},
	collisionbox = {0,0,0,0,0,0},

	damage_range = 2,
	velocity = 4,
	gravity = -0.001,

	damage = {
			fleshy=4,
			},

	damage_pass = {
			fleshy=2,
			},

	owner = 0,
	lifetime = 30,
	created = -1,
}

-------------------------------------------------------------------------------
-- name: MOBF_PLASMABALL_ENTITY.on_activate = function(self, staticdata)
--
--! @brief onactivate callback for plasmaball
--! @memberof MOBF_PLASMABALL_ENTITY
--! @private
--
--! @param self fireball itself
--! @param staticdata
-------------------------------------------------------------------------------
function MOBF_PLASMABALL_ENTITY.on_activate(self,staticdata)
	self.created = mobf_get_current_time()
end


-------------------------------------------------------------------------------
-- name: MOBF_PLASMABALL_ENTITY.on_step = function(self, dtime)
--
--! @brief onstep callback for plasmaball
--! @memberof MOBF_PLASMABALL_ENTITY
--! @private
--
--! @param self plasmaball itself
--! @param dtime time since last callback
-------------------------------------------------------------------------------
function MOBF_PLASMABALL_ENTITY.on_step(self, dtime)
	local pos = self.object:getpos()
	local node = minetest.get_node(pos)


	--detect hit
	local objs=minetest.get_objects_inside_radius({x=pos.x,y=pos.y,z=pos.z}, 1)

	local hit = false

	for k, obj in pairs(objs) do
		if obj:get_entity_name() ~= "mobf:plasmaball_entity" and
			obj ~= self.owner then
			hit=true
		end
	end

	--damage all objects not hit but at least passed
	mobf_do_area_damage(pos,self.owner,self.damage_pass,2)

	if hit then
		--damage objects within inner blast radius
		mobf_do_area_damage(pos,self.owner,self.damage,self.damage_range/4)

		--damage all objects within blast radius
		mobf_do_area_damage(pos,self.owner,self.damage,self.damage_range/2)
	end

	-- vanish when hitting a node
	if node.name ~= "air" or
		hit then

		--replace this loop by minetest.find_node_near?
		--do node damage
		for i=pos.x-1, pos.x+1, 1 do
			for j=pos.y-1, pos.y+1, 1 do
				for k=pos.z-1,pos.z+1,1 do
					--TODO create a little bit more sophisticated blast resistance
					if math.random() < 0.5 then
						local toremove = minetest.get_node({x=i,y=j,z=k})

						if toremove ~= nil and
							toremove.name ~= "default:stone" and
							toremove.name ~= "default:cobble" then

							minetest.remove_node({x=i,y=j,z=k})
						end
					end
				end
			end
		end

		self.object:remove()
	end

	--remove after lifetime has passed
	if self.created > 0 and
		self.created + self.lifetime < mobf_get_current_time() then
		self.object:remove()
	end
end

-- -----------------------------------------------------------------------------
-- Code Below is by far extent taken from throwing mod by PilzAdam
-- -----------------------------------------------------------------------------

mobf_assert_backtrace(MOBF_ARROW_ENTITY == nil)
--! @class MOBF_ARROW_ENTITY
--! @ingroup weapons
--! @brief a arrow entity
MOBF_ARROW_ENTITY={
	physical = false,
	timer=0,
	visual = "wielditem",
	visual_size = {x=0.1, y=0.1},
	textures = {"mobf:arrow_box"},
	lastpos={},
	collisionbox = {0,0,0,0,0,0},

	velocity =6,
	damage_groups = {
					fleshy=3,
					daemon=1.5
					},
	gravity  =9.81,
}

-------------------------------------------------------------------------------
-- name: MOBF_ARROW_ENTITY.on_step = function(self, dtime)
--
--! @brief onstep callback for arrow
--! @memberof MOBF_ARROW_ENTITY
--! @private
--
--! @param self arrow itself
--! @param dtime time since last callback
-------------------------------------------------------------------------------
MOBF_ARROW_ENTITY.on_step = function(self, dtime)
	self.timer=self.timer+dtime
	local pos = self.object:getpos()
	local node = minetest.get_node(pos)

	if self.timer>0.2 then
		local objs = minetest.get_objects_inside_radius({x=pos.x,y=pos.y,z=pos.z}, 2)
		for k, obj in pairs(objs) do
			if obj:get_luaentity() ~= nil and
				obj ~= self.owner then
				if obj:get_luaentity().name ~= "mobf:arrow_entity" and
					obj:get_luaentity().name ~= "__builtin:item" then
					obj:punch(self.object, 1.0, {
						full_punch_interval=1.0,
						damage_groups = self.damage_groups,
					}, nil)
					self.object:remove()
				end
			else
				--punch a player
				obj:punch(self.object, 1.0, {
					full_punch_interval=1.0,
					damage_groups = self.damage_groups,
				}, nil)
				self.object:remove()
			end
		end
	end

	if self.lastpos.x~=nil then
		if node.name ~= "air" then
			minetest.add_item(self.lastpos, 'mobf:arrow')
			self.object:remove()
		end
	end
	self.lastpos={x=pos.x, y=pos.y, z=pos.z}
end


-- moved due to doxygen problems
minetest.register_node("mobf:arrow_box", {
	drawtype = "nodebox",
	node_box = {
		type = "fixed",
		fixed = {
			-- Shaft
			{-6.5/17, -1.5/17, -1.5/17, 6.5/17, 1.5/17, 1.5/17},
			--Spitze
			{-4.5/17, 2.5/17, 2.5/17, -3.5/17, -2.5/17, -2.5/17},
			{-8.5/17, 0.5/17, 0.5/17, -6.5/17, -0.5/17, -0.5/17},
			--Federn
			{6.5/17, 1.5/17, 1.5/17, 7.5/17, 2.5/17, 2.5/17},
			{7.5/17, -2.5/17, 2.5/17, 6.5/17, -1.5/17, 1.5/17},
			{7.5/17, 2.5/17, -2.5/17, 6.5/17, 1.5/17, -1.5/17},
			{6.5/17, -1.5/17, -1.5/17, 7.5/17, -2.5/17, -2.5/17},

			{7.5/17, 2.5/17, 2.5/17, 8.5/17, 3.5/17, 3.5/17},
			{8.5/17, -3.5/17, 3.5/17, 7.5/17, -2.5/17, 2.5/17},
			{8.5/17, 3.5/17, -3.5/17, 7.5/17, 2.5/17, -2.5/17},
			{7.5/17, -2.5/17, -2.5/17, 8.5/17, -3.5/17, -3.5/17},
		}
	},
	tiles = {"mobf_arrow.png", "mobf_arrow.png", "mobf_arrow_back.png",
			"mobf_arrow_front.png", "mobf_arrow_2.png", "mobf_arrow.png"},
	groups = {not_in_creative_inventory=1},
})

local mods = minetest.get_modnames()
if mobf_contains(mods,"throwing") then
	print("throwing mod found2!")
	minetest.register_alias("mobf:arrow", "throwing:arrow")
end
