diff options
Diffstat (limited to 'item_tweaks')
-rw-r--r-- | item_tweaks/init.lua | 3 | ||||
-rw-r--r-- | item_tweaks/item_drop.lua | 314 | ||||
-rw-r--r-- | item_tweaks/sounds/item_drop.ogg | bin | 0 -> 7367 bytes | |||
-rw-r--r-- | item_tweaks/sounds/item_drop_pickup.1.ogg | bin | 0 -> 9981 bytes | |||
-rw-r--r-- | item_tweaks/sounds/item_drop_pickup.2.ogg | bin | 0 -> 10477 bytes | |||
-rw-r--r-- | item_tweaks/sounds/item_drop_pickup.3.ogg | bin | 0 -> 9586 bytes | |||
-rw-r--r-- | item_tweaks/sounds/item_drop_pickup.4.ogg | bin | 0 -> 9696 bytes |
7 files changed, 317 insertions, 0 deletions
diff --git a/item_tweaks/init.lua b/item_tweaks/init.lua new file mode 100644 index 0000000..efad17f --- /dev/null +++ b/item_tweaks/init.lua @@ -0,0 +1,3 @@ +local modpath = minetest.get_modpath(minetest.get_current_modname()) + +dofile(modpath.."/item_drop.lua") diff --git a/item_tweaks/item_drop.lua b/item_tweaks/item_drop.lua new file mode 100644 index 0000000..beed1c7 --- /dev/null +++ b/item_tweaks/item_drop.lua @@ -0,0 +1,314 @@ +-- item_drop code by cyisfor +-- https://github.com/cyisfor/item_drop + + +if drops == nil then + drops = {} +end + +local movers = {} + +local removedAlreadyDammit = {} + +vector.fixedNormalize = function(v) + assert(v) + local len = vector.length(v) + if len == 0 then + -- no length 1 vector will ever equal this + return vector.new(0,0,0) + else + return vector.divide(v, len) + end +end + +if not vector.dot then + vector.dot = function(p1,p2) + return p1.x * p2.x + p1.y * p2.y + p1.z * p2.z + end +end + +local function removeObject(object) + movers[object] = nil + removedAlreadyDammit[object] = true + object:remove() +end + +local function removeObjectWithSound(object) + movers[object] = nil + removedAlreadyDammit[object] = true + local pos=object:getpos() + minetest.sound_play("item_gone", { + pos=pos, + gain = 0.2, + max_hear_distance = 32, + }) + object:remove() +end + +-- returns whether the pickup failed or not. +-- nil pickupRadius means to infinity and beyond +local function pickup(player, inv, object, pickupRadius) + if removedAlreadyDammit[object] then + -- this gets called after the timeout, as well as when it hits the player + return true + end + if player == nil then return true end + + -- itemstring is serialized item so includes metadata + local lua = object:get_luaentity() + item = ItemStack(lua.itemstring) + if inv and inv:room_for_item("main", item) then + inv:add_item("main", item) + if object:get_luaentity().itemstring ~= "" then + minetest.sound_play("item_drop_pickup", { + to_player = player:get_player_name(), + gain = 0.4, + }) + end + lua.itemstring = '' + removeObject(object) + return false + else + return true + end +end + +local function isGood(object) + -- only want items swooping up after players, not after chests! + if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then + return true + else + return false + end +end + +local function stop(object) + movers[object] = nil + -- no pickup, even though it's close, so + -- stop moving towards the player + object:setvelocity({x=0, y=0, z=0}) + object:setacceleration({x=0, y=0, z=0}) + -- also we can walk on it and it can push pressure plates + -- physical_state = false means "please make us physical again" + local lua = object:get_luaentity() + if lua then + lua.physical_state = false + end + --object:set_properties({ + -- physical = true + --}) +end + +local function pickupOrStop(object, inv, player, pickupRadius) + local lua = object:get_luaentity() + if object == nil or lua == nil or lua.itemstring == nil then + return + end + if pickup(player, inv, object, pickupRadius) then + stop(object) + end +end + +-- GMass... it's the player's mass if the player were a giant planetlike object +-- which things orbit around +-- in the following units: +-- if G = 6.67×10−11 then +-- GMass = 1 for 14,992,503,748 kilograms +drops.playerGMass = 1.7 +-- the player is faaaaaaaat + +local function moveTowards(object, player, pickupRadius, attractRadius) + -- move it towards the player, then pick it up after a delay! + local pos1 = player:getpos() + if pos1 == nil then return end + local pos2 = object:getpos() + if pos2 == nil then return end + pos1.y = pos1.y+0.5 -- head towards player's belt + local direct = vector.subtract(pos1, pos2) + local R = vector.length(direct) + v = object:getvelocity() + stopped = v.x == 0 and v.y == 0 and v.z == 0 + -- when direction(X) = direction(V) we passed the player + -- so project V onto X. If same, passed. If not, approaching. + -- projection = norm(X) * (length(V) * cos(theta)) + -- => length(V) * dot(V,X) / length(V) / length(X) + -- = dot(V,X) / length(X) + -- if X is normalized, length(X) == 1 so... dot product! + -- sign(dot) > 0 = same direction sign(dot)< 0 = different + direct = vector.fixedNormalize(direct) + + -- idea, set velocity not acceleration but set it + -- not to velocity + acceleration, but to the projection of that + -- onto the direction vector. object will always have velocity towards YOU + + if R > attractRadius then + stop(object) + return + end + if R < pickupRadius or (not stopped and vector.dot(v,direct) < 0) then + pickupOrStop(object,player:get_inventory(),player,nil) + return + end + -- Fg = G*M1*M2/R^2 + -- M1*A1 = G * M1 * M2 / R^2 + -- A1 = G * M2 / R ^2 + -- G = whatever it takes for stuff to orbit around the player + -- and the weight of the player is ^^^ + -- A1 = C / R^2 + local A + A = drops.playerGMass / R^2 + A = math.max(A,2*drops.playerGMass) + object:setacceleration(vector.multiply(direct,A)) +end + +if minetest.setting_get("enable_item_pickup") == "true" then + local tickets = 0 -- XXX: oy vey + moveDelay = 0 + minetest.register_globalstep(function(dtime) + -- it's much more efficient to just restart... no way to unregister_globalstep right? + if not minetest.setting_get("enable_item_pickup") then return end + moveDelay = moveDelay + dtime + local pickupRadius = tonumber(minetest.setting_get("pickup_radius")) + local attractRadius = tonumber(minetest.setting_get("attract_radius")) + if not pickupRadius then pickupRadius = 0.5 end + if not attractRadius then attractRadius = 3 end + + if moveDelay > 0.1 then + moveDelay = 0 + -- correct your trajectory while moving + for object,pair in pairs(movers) do + local player = pair[1] + moveTowards(object,player,pickupRadius,attractRadius) + end + end + for _, player in ipairs(minetest.get_connected_players()) do + if player:get_hp() > 0 or not minetest.setting_getbool("enable_damage") then + local playerPosition = player:getpos() + if playerPosition ~= nil then + playerPosition.y = playerPosition.y + 0.5 + local inv = player:get_inventory() + + for _, object in ipairs(minetest.get_objects_inside_radius(playerPosition, 3)) do + if isGood(object) and (object:get_luaentity().dropped_by ~= player:get_player_name() or object:get_luaentity().age > 3) and + inv and + inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) + then + local ticket = tickets + movers[object] = {player,ticket} + tickets = tickets + 1 + moveTowards(object, player, pickupRadius, attractRadius) + -- make sure object doesn't push the player around! + object:get_luaentity().physical_state = true + object:get_luaentity().object:set_properties({ + physical = false, + collide_with_objects = false, + weight = 0 + }) + -- pleeease no immortal orbiting entities + -- unless you want them to be >:) + minetest.after(30, function(object) + -- only if it's still moving + -- but what if it started moving a second time? + pair = movers[object] + if pair and pair[2] == ticket then + stop(object) + end + end, object) + end + end + end + end + end + end) +end + +if minetest.setting_get("enable_item_drops") == "true" then + local old_handle_node_drops = minetest.handle_node_drops + + function new_handle_node_drops(pos, drops, digger) + if digger and digger.is_fake_player then + return old_handle_node_drops(pos, drops, digger) + end + local inv + -- the digger might be a node, like a constructor + if minetest.setting_getbool("creative_mode") and digger and digger:is_player() then + inv = digger:get_inventory() + end + for _, item in ipairs(drops) do + local count, name + if type(item) == "string" then + count = 1 + name = item + else + count = item:get_count() + name = item:get_name() + end + -- Only drop the item if not in creative, or if the item is not in creative inventory + if not inv or not inv:contains_item("main", ItemStack(name)) then + for i=1, count do + local obj = minetest.add_item(pos, item) + if obj ~= nil then + 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 + -- hurl it out into space at a random velocity + -- (still falling though) + obj:setvelocity({x=1/x, y=obj:getvelocity().y, z=1/z}) + end + end + end + end + -- the items have been dropped. Don't use builtin/item.lua or it could put the items + -- into an inventory! (see quarry) + -- return old_handle_node_drops(pos, drops, digger) + end + + function checkSetting(pos, drops, digger) + if minetest.setting_get("enable_item_drops") == "true" then + return new_handle_node_drops(pos, drops, digger) + else + return old_handle_node_drops(pos, drops, digger) + end + end + minetest.handle_node_drops = checkSetting + +end + +function minetest.item_drop(itemstack, dropper, pos) + if dropper and dropper.get_player_name then + local v = dropper:get_look_dir() + local p = {x=pos.x+v.x, y=pos.y+1.5+v.y, z=pos.z+v.z} + local r + if dropper:get_player_control().sneak then + r = itemstack + itemstack = itemstack:to_table() + itemstack.count = 1 + itemstack = ItemStack(itemstack) + r:take_item() + else + r = ItemStack("") + end + minetest.sound_play("item_drop", { + pos=pos, + gain = 1.0, + max_hear_distance = 32, + }) + local obj = minetest.add_item(p, itemstack) + if obj then + v.x = v.x*2 + v.y = v.y*2 + 1 + v.z = v.z*2 + obj:setvelocity(v) + obj:get_luaentity().dropped_by = dropper:get_player_name() + end + return r + else + minetest.add_item(pos, itemstack) + return ItemStack("") + end +end diff --git a/item_tweaks/sounds/item_drop.ogg b/item_tweaks/sounds/item_drop.ogg Binary files differnew file mode 100644 index 0000000..ced91f8 --- /dev/null +++ b/item_tweaks/sounds/item_drop.ogg diff --git a/item_tweaks/sounds/item_drop_pickup.1.ogg b/item_tweaks/sounds/item_drop_pickup.1.ogg Binary files differnew file mode 100644 index 0000000..2ae432d --- /dev/null +++ b/item_tweaks/sounds/item_drop_pickup.1.ogg diff --git a/item_tweaks/sounds/item_drop_pickup.2.ogg b/item_tweaks/sounds/item_drop_pickup.2.ogg Binary files differnew file mode 100644 index 0000000..f58bf08 --- /dev/null +++ b/item_tweaks/sounds/item_drop_pickup.2.ogg diff --git a/item_tweaks/sounds/item_drop_pickup.3.ogg b/item_tweaks/sounds/item_drop_pickup.3.ogg Binary files differnew file mode 100644 index 0000000..cf57c94 --- /dev/null +++ b/item_tweaks/sounds/item_drop_pickup.3.ogg diff --git a/item_tweaks/sounds/item_drop_pickup.4.ogg b/item_tweaks/sounds/item_drop_pickup.4.ogg Binary files differnew file mode 100644 index 0000000..bfe99d9 --- /dev/null +++ b/item_tweaks/sounds/item_drop_pickup.4.ogg |