diff --git a/README.txt b/README.txt old mode 100644 new mode 100755 diff --git a/init.lua b/init.lua old mode 100644 new mode 100755 index e4bce00..9e54705 --- a/init.lua +++ b/init.lua @@ -1,78 +1,125 @@ -minetest.register_globalstep(function(dtime) +item_drop = {} +local enable_damage = minetest.setting_getbool("enable_damage") +local creative_mode = minetest.setting_getbool("creative_mode") +local TICK_UPDATE = 0.1 + + +-- Following edits by gravgun + +item_drop.drop_callbacks = {} +item_drop.pickup_callbacks = {} + +-- on_drop(dropper, drop_entity, itemstack) +function item_drop.add_drop_callback(on_drop) + table.insert(item_drop.drop_callbacks, on_drop) +end + +-- on_pickup(picker, itemstack) +function item_drop.add_pickup_callback(on_pickup) + table.insert(item_drop.pickup_callbacks, on_pickup) +end + +-- Idea is to have a radius pickup range around the player, whatever the height +-- We need to have a radius that will at least contain 1 node distance at the player's feet +-- Using simple trigonometry, we get that we need a radius of +-- sqrt(pickup_range² + player_half_height²) +local pickup_range = 1.3 +local pickup_range_squared = pickup_range*pickup_range +local player_half_height = 0.9 +local scan_range = math.sqrt(player_half_height*player_half_height + pickup_range_squared) +-- Node drops are insta-pickup, everything else (player drops) are not +local delay_before_playerdrop_pickup = 1 +-- Time in which the node comes to the player +local pickup_duration = 0.1 +-- Little treshold so the items aren't already on the player's middle +local pickup_inv_duration = 1/pickup_duration*0.7 + +local function tick() + local tstamp = minetest.get_us_time() for _,player in ipairs(minetest.get_connected_players()) do - if player:get_hp() > 0 or not minetest.setting_getbool("enable_damage") then + if player:get_hp() > 0 or not enable_damage then local pos = player:getpos() - pos.y = pos.y+0.5 + pos.y = pos.y + player_half_height local inv = player:get_inventory() - - for _,object in ipairs(minetest.env:get_objects_inside_radius(pos, 1)) do - if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then - if inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then - inv:add_item("main", ItemStack(object:get_luaentity().itemstring)) - if object:get_luaentity().itemstring ~= "" then - minetest.sound_play("item_drop_pickup", { - to_player = player:get_player_name(), - gain = 0.4, - }) - end - object:get_luaentity().itemstring = "" - object:remove() - end - end - end - - for _,object in ipairs(minetest.env:get_objects_inside_radius(pos, 2)) do - if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then - if object:get_luaentity().collect then - if inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then - local pos1 = pos - pos1.y = pos1.y+0.2 + + if inv then + for _,object in ipairs(minetest.get_objects_inside_radius(pos, scan_range)) do + local luaEnt = object:get_luaentity() + if luaEnt and luaEnt.name == "__builtin:item" then + local ticky = luaEnt.item_drop_min_tstamp + if ticky then + if tstamp >= ticky then + luaEnt.item_drop_min_tstamp = nil + end + elseif not luaEnt.item_drop_nopickup then + -- Point-line distance computation, heavily simplified since the wanted line, + -- being the player, is completely upright (no variation on X or Z) local pos2 = object:getpos() - local vec = {x=pos1.x-pos2.x, y=pos1.y-pos2.y, z=pos1.z-pos2.z} - vec.x = vec.x*3 - vec.y = vec.y*3 - vec.z = vec.z*3 - object:setvelocity(vec) - object:get_luaentity().physical_state = false - object:get_luaentity().object:set_properties({ - physical = false - }) - - minetest.after(1, function(args) - local lua = object:get_luaentity() - if object == nil or lua == nil or lua.itemstring == nil then - return - end - if inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then - inv:add_item("main", ItemStack(object:get_luaentity().itemstring)) - if object:get_luaentity().itemstring ~= "" then - minetest.sound_play("item_drop_pickup", { - to_player = player:get_player_name(), - gain = 0.4, - }) - end - object:get_luaentity().itemstring = "" - object:remove() - else - object:setvelocity({x=0,y=0,z=0}) - object:get_luaentity().physical_state = true - object:get_luaentity().object:set_properties({ - physical = true + -- No sqrt, avoid useless computation + -- (just take the radius, compare it to the square of what you want) + -- Pos order doesn't really matter, we're squaring the result + -- (but don't change it, we use the cached values afterwards) + local dX = pos.x-pos2.x + local dZ = pos.z-pos2.z + local playerDistance = dX*dX+dZ*dZ + if playerDistance <= pickup_range_squared then + local itemStack = ItemStack(luaEnt.itemstring) + if inv:room_for_item("main", itemStack) then + local vec = {x=dX, y=pos.y-pos2.y, z=dZ} + vec.x = vec.x*pickup_inv_duration + vec.y = vec.y*pickup_inv_duration + vec.z = vec.z*pickup_inv_duration + object:setvelocity(vec) + luaEnt.physical_state = false + luaEnt.object:set_properties({ + physical = false }) + -- Mark the object as already picking up + luaEnt.item_drop_nopickup = true + + minetest.after(pickup_duration, function() + local lua = luaEnt + if object == nil or lua == nil or lua.itemstring == nil then + return + end + if inv:room_for_item("main", itemStack) then + inv:add_item("main", itemStack) + if luaEnt.itemstring ~= "" then + minetest.sound_play("item_drop_pickup", {pos = pos, gain = 0.3, max_hear_distance = 8}) + end + luaEnt.itemstring = "" + object:remove() + for i, cb in ipairs(item_drop.pickup_callbacks) do + cb(player, itemstack) + end + else + object:setvelocity({x = 0,y = 0,z = 0}) + luaEnt.physical_state = true + luaEnt.object:set_properties({ + physical = true + }) + luaEnt.item_drop_nopickup = nil + end + end) end - end, {player, object}) - + end end end end end end end -end) + minetest.after(TICK_UPDATE, tick) +end +local mt_handle_node_drops = minetest.handle_node_drops function minetest.handle_node_drops(pos, drops, digger) + if digger and digger.is_fake_player then -- Pipeworks' wielders + mt_handle_node_drops(pos, drops, digger) + return + end local inv - if minetest.setting_getbool("creative_mode") and digger and digger:is_player() then + if creative_mode and digger and digger:is_player() then inv = digger:get_inventory() end for _,item in ipairs(drops) do @@ -86,31 +133,52 @@ function minetest.handle_node_drops(pos, drops, digger) end if not inv or not inv:contains_item("main", ItemStack(name)) then for i=1,count do - local obj = minetest.env:add_item(pos, name) + local obj + local x = math.random(1, 5) + if math.random(1,2) == 1 then x = -x end + + local z = math.random(1, 5) + if math.random(1,2) == 1 then z = -z end + + obj = minetest.spawn_item(pos, name) if obj ~= nil then - obj:get_luaentity().collect = true - local x = math.random(1, 5) - if math.random(1,2) == 1 then - x = -x - end - local z = math.random(1, 5) - if math.random(1,2) == 1 then - z = -z - end obj:setvelocity({x=1/x, y=obj:getvelocity().y, z=1/z}) - - -- FIXME this doesnt work for deactiveted objects - if minetest.setting_get("remove_items") and tonumber(minetest.setting_get("remove_items")) then - minetest.after(tonumber(minetest.setting_get("remove_items")), function(obj) - obj:remove() - end, obj) - end end end end end end -if minetest.setting_get("log_mods") then - minetest.log("action", "item_drop loaded") +local mt_item_drop = minetest.item_drop +function minetest.item_drop(itemstack, dropper, pos) + if dropper and dropper.is_player then + local v = dropper:get_look_dir() + local p = {x=pos.x, y=pos.y+1.2, z=pos.z} + local cs = itemstack:get_count() + if dropper:get_player_control().sneak then + cs = 1 + end + local item = itemstack:take_item(cs) + local obj = core.add_item(p, item) + if obj then + v.x = v.x*2 + v.y = v.y*2 + 2 + v.z = v.z*2 + obj:setvelocity(v) + obj:get_luaentity().item_drop_min_tstamp = minetest.get_us_time() + delay_before_playerdrop_pickup * 1000000 + for i, cb in ipairs(item_drop.drop_callbacks) do + cb(dropper, obj, itemstack) + end + end + else + core.add_item(pos, itemstack) + end + return itemstack end + +if minetest.setting_getbool("log_mods") then + minetest.log("action", "[item_drop] item_drop overriden: " .. tostring(mt_item_drop) .. " " .. tostring(minetest.item_drop)) + minetest.log("action", "[item_drop] loaded.") +end + +tick() diff --git a/sounds/item_drop_pickup.1.ogg b/sounds/item_drop_pickup.1.ogg old mode 100644 new mode 100755 index 2ae432d..6b335ea Binary files a/sounds/item_drop_pickup.1.ogg and b/sounds/item_drop_pickup.1.ogg differ diff --git a/sounds/item_drop_pickup.2.ogg b/sounds/item_drop_pickup.2.ogg old mode 100644 new mode 100755 index f58bf08..7b7e169 Binary files a/sounds/item_drop_pickup.2.ogg and b/sounds/item_drop_pickup.2.ogg differ diff --git a/sounds/item_drop_pickup.3.ogg b/sounds/item_drop_pickup.3.ogg old mode 100644 new mode 100755 index cf57c94..1245ff5 Binary files a/sounds/item_drop_pickup.3.ogg and b/sounds/item_drop_pickup.3.ogg differ diff --git a/sounds/item_drop_pickup.4.ogg b/sounds/item_drop_pickup.4.ogg old mode 100644 new mode 100755 index bfe99d9..1ffa2c1 Binary files a/sounds/item_drop_pickup.4.ogg and b/sounds/item_drop_pickup.4.ogg differ