From 15584dad88fabefd9f6a6bde760932fbfdde5769 Mon Sep 17 00:00:00 2001
From: teknomunk <teknomunk@protonmail.com>
Date: Mon, 11 Mar 2024 07:26:51 +0000
Subject: [PATCH] Implement custom item dropper handlers, implement droppers
 placing minecarts

---
 mods/ENTITIES/mcl_minecarts/init.lua      | 14 ++++++++
 mods/ITEMS/REDSTONE/mcl_droppers/init.lua | 41 ++++++++++++++++++-----
 2 files changed, 47 insertions(+), 8 deletions(-)

diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua
index 1346e502b..6d31a1fa1 100644
--- a/mods/ENTITIES/mcl_minecarts/init.lua
+++ b/mods/ENTITIES/mcl_minecarts/init.lua
@@ -850,6 +850,19 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer)
 	return itemstack
 end
 
+local function dropper_place_minecart(dropitem, pos)
+	local node = minetest.get_node(pos)
+	local nodedef = minetest.registered_nodes[node.name]
+
+	-- Don't try to place the minecart if pos isn't a rail
+	if (nodedef.groups.rail or 0) == 0 then return false end
+
+	mcl_minecarts.place_minecart(dropitem, {
+		above = pos,
+		under = vector.offset(pos,0,-1,0)
+	})
+	return true
+end
 
 local function register_craftitem(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative)
 	entity_mapping[itemstring] = entity_id
@@ -860,6 +873,7 @@ local function register_craftitem(itemstring, entity_id, description, tt_help, l
 	end
 	local def = {
 		stack_max = 1,
+		_mcl_dropper_on_drop = dropper_place_minecart,
 		on_place = function(itemstack, placer, pointed_thing)
 			if not pointed_thing.type == "node" then
 				return
diff --git a/mods/ITEMS/REDSTONE/mcl_droppers/init.lua b/mods/ITEMS/REDSTONE/mcl_droppers/init.lua
index 625549a1d..13a5b879d 100644
--- a/mods/ITEMS/REDSTONE/mcl_droppers/init.lua
+++ b/mods/ITEMS/REDSTONE/mcl_droppers/init.lua
@@ -132,9 +132,9 @@ local dropperdef = {
 				-- If they are containers - double down as hopper
 				mcl_util.hopper_push(pos, droppos)
 			end
-			if dropnodedef.walkable then
-				return
-			end
+			if dropnodedef.walkable then return end
+
+			-- Build a list of items in the dropper
 			local stacks = {}
 			for i = 1, inv:get_size("main") do
 				local stack = inv:get_stack("main", i)
@@ -142,17 +142,36 @@ local dropperdef = {
 					table.insert(stacks, { stack = stack, stackpos = i })
 				end
 			end
+
+			-- Pick an item to drop
+			local dropitem = nil
+			local stack = nil
+			local r = nil
 			if #stacks >= 1 then
-				local r = math.random(1, #stacks)
-				local stack = stacks[r].stack
-				local dropitem = ItemStack(stack)
+				r = math.random(1, #stacks)
+				stack = stacks[r].stack
+				dropitem = ItemStack(stack)
 				local stackdef = core.registered_items[stack:get_name()]
 				if not stackdef then
 					return
 				end
-
 				dropitem:set_count(1)
-				local stack_id = stacks[r].stackpos
+			end
+			if not dropitem then return end
+
+			-- Flag for if the item was dropped. If true the item will be removed from
+			-- the inventory after dropping
+			local item_dropped = false
+
+			-- Check if the drop item has a custom handler
+			local itemdef = minetest.registered_craftitems[dropitem:get_name()]
+			if itemdef._mcl_dropper_on_drop then
+				item_dropped = itemdef._mcl_dropper_on_drop(dropitem, droppos)
+			end
+
+			-- If a custom handler wasn't successful then drop the item as an entity
+			if not item_dropped then
+				-- Drop as entity
 				local pos_variation = 100
 				droppos = vector.offset(droppos,
 					math.random(-pos_variation, pos_variation) / 1000,
@@ -164,6 +183,12 @@ local dropperdef = {
 				local speed = 3
 				item_entity:set_velocity(vector.multiply(drop_vel, speed))
 				stack:take_item()
+				item_dropped = trie
+			end
+
+			-- Remove dropped items from inventory
+			if item_dropped then
+				local stack_id = stacks[r].stackpos
 				inv:set_stack("main", stack_id, stack)
 			end
 		end,