Refactor item magnet and fix multi-player bug

The bug was that the item misbehaved when multiple players were close to
an item entity
master
Wuzzy 2022-05-21 18:06:03 +02:00
parent 928a1e7692
commit 8231f21808
8 changed files with 166 additions and 155 deletions

View File

@ -1,8 +1,24 @@
Builtin item mod
================
By PilzAdam
Tweaked by Kaadmy, for Pixture
Repixture builtin item mod
==========================
Items are now destroyed by lava and flow with water.
Item entities for Repixture.
Source license: LGPLv2.1
Adds the custom handling for item entities (dropped items) by overriding
`__builtin:item`. Item entities work similar to Minetest's builtin item entities.
Features:
* Basic physics (affected by gravity, collides)
* Supports the `item_entity_ttl` setting (auto-delete item after some time)
* Item magnet (player collects item automatically when close)
* Item is destroyed by lava, fire or nodes that deal damage with `damage_per_second`
* Notifies rp_nav when the map item was collected
* If the group `no_item_drop` is present in the item definition, or
the item entity will be instantly deleted
## Licensing
Credits: Originally by PilzAdam (released under the name `builtin_item`),
then tweaked by Kaadmy for Pixture.
Source code license: LGPLv2.1
Media license: CC BY-SA 4.0

View File

@ -6,6 +6,28 @@
--
local GRAVITY = tonumber(minetest.settings:get("movement_gravity")) or 9.81
local nav_mod = minetest.get_modpath("rp_nav") ~= nil
-- Distance from player to item
-- below which the item magnet kicks in
-- and starts attracting the item.
local ITEM_MAGNET_ACTIVE_DISTANCE = 1.5
-- Distance from player to item
-- below which the player's item magnet
-- will collect the item into the inventory.
local ITEM_MAGNET_COLLECT_DISTANCE = 0.5
-- Distance above ground at which players
-- will collect items
local ITEM_MAGNET_HAND_HEIGHT = 0.5
-- Movement speed at which the item
-- magnet attracts items
local ITEM_MAGNET_ATTRACT_SPEED = 5
local function add_item_death_particle(ent)
minetest.add_particle({
pos = ent.object:get_pos(),
@ -33,7 +55,7 @@ minetest.register_entity(
timer = 0,
item_magnet_timer = 0,
},
itemstring = "",
physical_state = true,
item_magnet = false, -- set by other mod that implements item magnet
@ -99,45 +121,135 @@ minetest.register_entity(
self.object:set_acceleration({x=0, y=-GRAVITY, z=0})
self:set_item(self.itemstring)
end,
on_step = function(self, dtime)
local itempos = self.object:get_pos()
-- Remove item if old
local time_to_live = tonumber(minetest.settings:get("item_entity_ttl"))
if not time_to_live then time_to_live = 900 end
if not self.timer then self.timer = 0 end
if not self.item_magnet_timer then self.item_magnet_timer = 0 end
self.timer = self.timer + dtime
if self.item_magnet_timer >= 0 then
self.item_magnet_timer = self.item_magnet_timer - dtime
end
if time_to_live ~= -1 and (self.timer > time_to_live) then
add_item_death_particle(self)
minetest.log("action", "[rp_builtin_item] Item entity removed due to timeout at "..minetest.pos_to_string(self.object:get_pos()))
self.object:remove()
return
end
local p = self.object:get_pos()
local name = minetest.get_node(p).name
local def = minetest.registered_nodes[name]
-- Destroy item in damaging node
if def and def.damage_per_second > 0 then
if minetest.get_item_group(name, "lava") ~= 0 or minetest.get_item_group(name, "fire") ~= 0 then
minetest.sound_play("builtin_item_lava", {pos = self.object:get_pos(), gain = 0.45})
end
add_item_death_particle(self)
minetest.log("action", "[rp_builtin_item] Item entity destroyed in damaging node at "..minetest.pos_to_string(self.object:get_pos()))
minetest.log("action", "[rp_builtin_item] Item entity removed due to timeout at "..minetest.pos_to_string(itempos))
self.object:remove()
return
end
if self.item_magnet then
local nodename = minetest.get_node(itempos).name
local def = minetest.registered_nodes[nodename]
-- Destroy item in damaging node
if def and def.damage_per_second > 0 then
if minetest.get_item_group(nodename, "lava") ~= 0 or minetest.get_item_group(nodename, "fire") ~= 0 then
minetest.sound_play("builtin_item_lava", {pos = itempos, gain = 0.45})
end
add_item_death_particle(self)
minetest.log("action", "[rp_builtin_item] Item entity destroyed in damaging node at "..minetest.pos_to_string(itempos))
self.object:remove()
return
end
p.y = p.y - 0.3
local nn = minetest.get_node(p).name
-- Item magnet: Attract item to closest living player
local object = self.object
local objects_around = minetest.get_objects_inside_radius(self.object:get_pos(), ITEM_MAGNET_ACTIVE_DISTANCE)
local closest_dist = math.huge
local closest_player = nil
local playerpos
for o=1, #objects_around do
local player = objects_around[o]
if player:is_player() then
playerpos = player:get_pos()
playerpos.y = playerpos.y + ITEM_MAGNET_HAND_HEIGHT
if vector.distance(playerpos, itempos) < closest_dist and player:get_hp() > 0 then
closest_dist = vector.distance(playerpos, itempos)
closest_player = player
end
end
end
local lua = object:get_luaentity()
if object == nil or lua == nil or lua.itemstring == nil then
return
end
-- Item magnet handling
local len, vec
if closest_player then
--playerpos.y = playerpos.y + ITEM_MAGNET_HAND_HEIGHT
vec = {
x = playerpos.x - itempos.x,
y = playerpos.y - itempos.y,
z = playerpos.z - itempos.z
}
len = vector.length(vec)
end
if closest_player ~= nil and lua.item_magnet_timer <= 0 and len < ITEM_MAGNET_ACTIVE_DISTANCE then
local inv = closest_player:get_inventory()
-- Activate item magnet
if inv and inv:room_for_item("main", ItemStack(lua.itemstring)) then
if len >= ITEM_MAGNET_COLLECT_DISTANCE then
-- Attract item to player
vec = vector.divide(vec, len) -- It's a normalize but we have len yet (vector.normalize(vec))
vec.x = vec.x*ITEM_MAGNET_ATTRACT_SPEED
vec.y = vec.y*ITEM_MAGNET_ATTRACT_SPEED
vec.z = vec.z*ITEM_MAGNET_ATTRACT_SPEED
object:set_velocity(vec)
object:set_properties({ physical = false })
self.item_magnet = true
return
else
-- Player collects item if close enough
if inv:room_for_item("main", ItemStack(lua.itemstring)) then
if minetest.is_creative_enabled(closest_player:get_player_name()) then
if not inv:contains_item("main", ItemStack(lua.itemstring), true) then
inv:add_item("main", ItemStack(lua.itemstring))
end
else
inv:add_item("main", ItemStack(lua.itemstring))
end
if lua.itemstring ~= "" then
minetest.sound_play(
"builtin_item_pickup",
{
pos = itempos,
gain = 0.3,
max_hear_distance = 16
}, true)
end
-- Notify nav mod of inventory change
if nav_mod and lua.itemstring == "rp_nav:map" then
nav.map.update_hud_flags(closest_player)
end
lua.itemstring = ""
object:remove()
return
end
return
end
end
else
-- Deactivate item magnet if out of range
if lua.item_magnet then
object:set_velocity({x = 0, y = object:get_velocity().y, z = 0})
lua.item_magnet = false
end
end
itempos.y = itempos.y - 0.3
local nn = minetest.get_node(itempos).name
-- If node is not registered or node is walkably solid:
if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable then
if self.physical_state then

View File

@ -1,7 +1,12 @@
Item drop mod
=============
By PilzAdam
Tweaked by Kaadmy, for Pixture
Asset license: CC BY-SA 4.0
Adds custom item drop handling.
If a node is dug, its drop will appear as items on the ground.
## License
Credits: Originally by PilzAdam, then
tweaked by Kaadmy for Pixture
Source license: LGPLv2.1

View File

@ -1,36 +1,10 @@
--
-- Item drop mod
-- By PilzAdam
-- Tweaked by Kaadmy, for Pixture
--
local nav_mod = minetest.get_modpath("rp_nav") ~= nil
item_drop = {}
-- Distance from player to item
-- below which the item magnet kicks in
-- and starts attracting the item.
local ITEM_MAGNET_ACTIVE_DISTANCE = 1.5
-- Distance from player to item
-- below which the player's item magnet
-- will collect the item into the inventory.
local ITEM_MAGNET_COLLECT_DISTANCE = 0.5
-- Distance above ground at which players
-- will collect items
local ITEM_MAGNET_HAND_HEIGHT = 0.5
-- Movement speed at which the item
-- magnet attracts items
local ITEM_MAGNET_ATTRACT_SPEED = 5
-- Time in seconds for which the item magnet is
-- inactive after being dropped by a player
local ITEM_MAGNET_DELAY_AFTER_DROP = 1.5
item_drop = {}
function item_drop.drop_item(pos, itemstack)
local rpos = {
x = pos.x + math.random(-0.3, 0.3),
@ -88,102 +62,6 @@ minetest.item_drop = function(itemstack, dropper, pos)
end
end
local function valid(object)
local ent = object:get_luaentity()
return ent.timer ~= nil and ent.item_magnet_timer ~= nil
end
minetest.register_globalstep(
function(dtime)
for _,player in ipairs(minetest.get_connected_players()) do
if player:get_hp() > 0 or not minetest.settings:get_bool("enable_damage") then
local pos = player:get_pos()
local inv = player:get_inventory()
local in_radius = minetest.get_objects_inside_radius(pos, 6.0)
for _,object in ipairs(in_radius) do
if not object:is_player() and object:get_luaentity()
and object:get_luaentity().name == "__builtin:item" and valid(object) then
local pos1 = table.copy(pos)
pos1.y = pos1.y + ITEM_MAGNET_HAND_HEIGHT
local pos2 = object:get_pos()
local vec = {
x = pos1.x - pos2.x,
y = pos1.y - pos2.y,
z = pos1.z - pos2.z
}
local len = vector.length(vec)
local lua = object:get_luaentity()
if object == nil or lua == nil or lua.itemstring == nil then
return
end
-- Item magnet handling
if len < ITEM_MAGNET_ACTIVE_DISTANCE and lua.item_magnet_timer <= 0 then
-- Activate item magnet
if inv and inv:room_for_item("main", ItemStack(lua.itemstring)) then
if len >= ITEM_MAGNET_COLLECT_DISTANCE then
-- Attract item to player
vec = vector.divide(vec, len) -- It's a normalize but we have len yet (vector.normalize(vec))
vec.x = vec.x*ITEM_MAGNET_ATTRACT_SPEED
vec.y = vec.y*ITEM_MAGNET_ATTRACT_SPEED
vec.z = vec.z*ITEM_MAGNET_ATTRACT_SPEED
lua.item_magnet = true
object:set_velocity(vec)
object:set_properties({ physical = false })
else
-- Player collects item if close enough
if inv:room_for_item("main", ItemStack(lua.itemstring)) then
if minetest.is_creative_enabled(player:get_player_name()) then
if not inv:contains_item("main", ItemStack(lua.itemstring), true) then
inv:add_item("main", ItemStack(lua.itemstring))
end
else
inv:add_item("main", ItemStack(lua.itemstring))
end
if lua.itemstring ~= "" then
minetest.sound_play(
"item_drop_pickup",
{
pos = pos,
gain = 0.3,
max_hear_distance = 16
}, true)
end
-- Notify nav mod of inventory change
if nav_mod and lua.itemstring == "rp_nav:map" then
nav.map.update_hud_flags(player)
end
lua.itemstring = ""
object:remove()
end
end
end
else
-- Deactivate item magnet if out of range
if lua.item_magnet then
object:set_velocity({x = 0, y = object:get_velocity().y, z = 0})
lua.item_magnet = false
end
end
end
end
end
end
end)
function minetest.handle_node_drops(pos, drops, digger)
-- If digger is in Creative Mode, give items directly to digger
if digger and digger:is_player() and minetest.is_creative_enabled(digger:get_player_name()) then