2 collection.collection_height = 0.5 -- the height of the collection based off the player's origin y height
3 collection.magnet_radius = 2 -- the radius of the item magnet
4 collection.allow_lower = false -- false = items below origin y of player will not be collected --true = player will collect all objects in radius --false = minecraft style --true = pilzadam style
5 collection.collection_time = 2.5 --the time which the item will be collected
7 local path = minetest.get_modpath("itemhandling")
8 dofile(path.."/magnet.lua")
11 local creative_mode = minetest.settings:get_bool("creative_mode")
15 if not creative_mode then
16 function minetest.handle_node_drops(pos, drops, digger)
17 local meta = digger:get_wielded_item():get_meta()
18 local slippery = meta:get_int("slippery")
19 local careful = meta:get_int("careful")
20 local fortune = meta:get_int("fortune") + 1
21 local autorepair = meta:get_int("autorepair")
22 local spiky = meta:get_int("spiky")
24 drops = {minetest.get_node(pos).name}
27 for _,item in ipairs(drops) do
29 if type(item) == "string" then
33 count = item:get_count()
34 name = item:get_name()
37 local obj = minetest.add_item(pos, name)
39 local x=math.random(-2,2)*math.random()
40 local y=math.random(2,5)
41 local z=math.random(-2,2)*math.random()
42 obj:set_velocity({x=x, y=y, z=z})
46 local experience_amount = minetest.get_item_group(minetest.get_node(pos).name,"experience")
47 if experience_amount > 0 then
48 minetest.throw_experience(pos, experience_amount)
51 --make the player drop their "slippery" item
52 if slippery > 0 and math.random(0,1000) < slippery then
53 minetest.item_drop(digger:get_wielded_item(), digger, digger:get_pos())
54 digger:set_wielded_item("")
57 --auto repair the item
58 if autorepair > 0 and math.random(0,1000) < autorepair then
59 local itemstack = digger:get_wielded_item()
60 itemstack:add_wear(autorepair*-100)
61 digger:set_wielded_item(itemstack)
64 --hurt the player randomly
65 if spiky > 0 and math.random(0,1000) < spiky then
66 digger:set_hp(digger:get_hp()-spiky)
71 function minetest.handle_node_drops(pos, drops, digger)
73 minetest.register_on_dignode(function(pos, oldnode, digger)
74 if digger and digger:is_player() then
75 local inv = digger:get_inventory()
76 if inv and not inv:contains_item("main", oldnode) and inv:room_for_item("main", oldnode) then
77 inv:add_item("main", oldnode)
81 minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
82 return(itemstack:get_name())
86 -- Minetest: builtin/item_entity.lua
88 function minetest.spawn_item(pos, item)
89 -- Take item in any format
90 local stack = ItemStack(item)
91 local obj = minetest.add_entity(pos, "__builtin:item")
92 -- Don't use obj if it couldn't be added to the map.
94 obj:get_luaentity():set_item(stack:to_string())
99 function minetest.throw_item(pos, item)
100 -- Take item in any format
101 local stack = ItemStack(item)
102 local obj = minetest.add_entity(pos, "__builtin:item")
103 -- Don't use obj if it couldn't be added to the map.
105 obj:get_luaentity():set_item(stack:to_string())
106 local x=math.random(-2,2)*math.random()
107 local y=math.random(2,5)
108 local z=math.random(-2,2)*math.random()
109 obj:set_velocity({x=x, y=y, z=z})
115 function minetest.throw_experience(pos, amount)
117 local obj = minetest.add_entity(pos, "experience:orb")
118 -- Don't use obj if it couldn't be added to the map.
120 local x=math.random(-2,2)*math.random()
121 local y=math.random(2,5)
122 local z=math.random(-2,2)*math.random()
123 obj:set_velocity({x=x, y=y, z=z})
130 function minetest.item_drop(itemstack, dropper, pos)
131 local dropper_is_player = dropper and dropper:is_player()
132 local p = table.copy(pos)
134 if dropper_is_player then
135 local sneak = dropper:get_player_control().sneak
138 cnt = itemstack:get_count()
143 cnt = itemstack:get_count()
145 local item = itemstack:take_item(cnt)
146 local obj = minetest.add_item(p, item)
148 if dropper_is_player then
149 local dir = dropper:get_look_dir()
151 dir.y = dir.y * 2.9 + 2
153 dir = vector.add(dir,dropper:get_player_velocity())
154 obj:set_velocity(dir)
155 obj:get_luaentity().dropped_by = dropper:get_player_name()
156 obj:get_luaentity().collection_timer = 0
160 -- If we reach this, adding the object to the
161 -- environment failed
164 -- If item_entity_ttl is not set, enity will have default life time
165 -- Setting it to -1 disables the feature
167 local time_to_live = tonumber(minetest.settings:get("item_entity_ttl")) or 300
168 local gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.81
171 minetest.register_entity(":__builtin:item", {
172 initial_properties = {
175 collide_with_objects = false,
176 collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
177 visual = "wielditem",
178 visual_size = {x = 0.4, y = 0.4},
180 spritediv = {x = 1, y = 1},
181 initial_sprite_basepos = {x = 0, y = 0},
188 slippery_state = false,
189 physical_state = true,
192 -- Pushing item out of solid nodes
194 force_out_start = nil,
195 --Collection Variables
196 collection_timer = 2,
197 collection_timer_goal = collection.collection_time,
198 collection_height = collection.collection_height,
203 radius = collection.magnet_radius,
204 time_to_live = time_to_live,
206 set_item = function(self, item)
207 local stack = ItemStack(item or self.itemstring)
208 self.itemstring = stack:to_string()
209 if self.itemstring == "" then
210 -- item not yet known
214 -- Backwards compatibility: old clients use the texture
215 -- to get the type of the item
216 local itemname = stack:is_known() and stack:get_name() or "unknown"
218 local max_count = stack:get_stack_max()
219 local count = math.min(stack:get_count(), max_count)
222 local coll_height = size * 0.75
223 local def = minetest.registered_nodes[itemname]
224 local glow = def and def.light_source
226 self.object:set_properties({
228 visual = "wielditem",
229 textures = {itemname},
230 visual_size = {x = size, y = size},
231 collisionbox = {-size, -0.21, -size,
232 size, coll_height, size},
233 selectionbox = {-size, -size, -size, size, size, size},
234 automatic_rotate = math.pi * 0.5 * 0.2 / size,
235 wield_item = self.itemstring,
241 get_staticdata = function(self)
242 return minetest.serialize({
243 itemstring = self.itemstring,
245 dropped_by = self.dropped_by,
246 collection_timer = self.collection_timer,
247 collectable = self.collectable,
248 try_timer = self.try_timer,
249 collected = self.collected,
250 delete_timer = self.delete_timer,
251 collector = self.collector,
252 magnet_timer = self.magnet_timer,
256 on_activate = function(self, staticdata, dtime_s)
257 if string.sub(staticdata, 1, string.len("return")) == "return" then
258 local data = minetest.deserialize(staticdata)
259 if data and type(data) == "table" then
260 self.itemstring = data.itemstring
261 self.age = (data.age or 0) + dtime_s
262 self.dropped_by = data.dropped_by
263 self.magnet_timer = data.magnet_timer
264 self.collection_timer = data.collection_timer
265 self.collectable = data.collectable
266 self.try_timer = data.try_timer
267 self.collected = data.collected
268 self.delete_timer = data.delete_timer
269 self.collector = data.collector
270 --print("restored timer: "..self.collection_timer)
273 self.itemstring = staticdata
275 local x=math.random(-2,2)*math.random()
276 local y=math.random(2,5)
277 local z=math.random(-2,2)*math.random()
278 self.object:set_velocity(vector.new(x,y,z))
279 -- print(self.collection_timer)
281 self.object:set_armor_groups({immortal = 1})
282 self.object:set_velocity({x = 0, y = 2, z = 0})
283 self.object:set_acceleration({x = 0, y = -gravity, z = 0})
287 enable_physics = function(self)
288 if not self.physical_state then
289 self.physical_state = true
290 self.object:set_properties({physical = true})
291 self.object:set_velocity({x=0, y=0, z=0})
292 self.object:set_acceleration({x=0, y=-gravity, z=0})
296 disable_physics = function(self)
297 if self.physical_state then
298 self.physical_state = false
299 self.object:set_properties({physical = false})
300 self.object:set_velocity({x=0, y=0, z=0})
301 self.object:set_acceleration({x=0, y=0, z=0})
305 on_step = function(self, dtime,moveresult)
306 --if item set to be collected then only execute go to player
307 if self.collected == true then
308 if not self.collector then
312 local collector = minetest.get_player_by_name(self.collector)
314 self.magnet_timer = self.magnet_timer + dtime
315 self.object:set_acceleration(vector.new(0,0,0))
316 self.disable_physics(self)
318 local pos = self.object:get_pos()
319 local pos2 = collector:get_pos()
320 local player_velocity = collector:get_player_velocity()
321 pos2.y = pos2.y + self.collection_height
323 local direction = vector.normalize(vector.subtract(pos2,pos))
324 local distance = vector.distance(pos2,pos)
326 --remove if too far away
327 if distance > self.radius then
331 local multiplier = (self.radius*5) - distance
332 local velocity = vector.multiply(direction,multiplier)
334 local velocity = vector.add(player_velocity,velocity)
336 self.object:set_velocity(velocity)
338 if distance < 0.3 or self.magnet_timer > 0.2 or (self.old_magnet_distance and self.old_magnet_distance < distance) then
342 self.old_magnet_distance = distance
343 --self.delete_timer = self.delete_timer + dtime
344 --this is where the item gets removed from world
345 --if self.delete_timer > 1 then
346 -- self.object:remove()
350 --print(self.collector.." does not exist")
355 --allow entity to be collected after timer
356 if self.collectable == false and self.collection_timer >= self.collection_timer_goal then
357 self.collectable = true
358 elseif self.collectable == false then
359 self.collection_timer = self.collection_timer + dtime
362 self.age = self.age + dtime
363 if self.time_to_live > 0 and self.age > self.time_to_live then
369 local pos = self.object:get_pos()
370 --stop crashing if this ever fails
374 local node = minetest.get_node_or_nil({
376 y = pos.y + self.object:get_properties().collisionbox[2] - 0.05,
381 -- Remove nodes in 'ignore'
382 if node and node.name == "ignore" then
388 --burn inside fire nodes
389 local node_inside = minetest.get_node_or_nil(pos)
390 if node_inside and (node_inside.name == "fire:fire" or node_inside.name == "nether:lava" or node_inside.name == "nether:lavaflow" or node_inside.name == "main:lava" or node_inside.name == "main:lavaflow") then
391 minetest.add_particlespawner({
396 minvel = vector.new(-1,0.5,-1),
397 maxvel = vector.new(1,1,1),
398 minacc = {x=0, y=1, z=0},
399 maxacc = {x=0, y=2, z=0},
404 collisiondetection = false,
406 texture = "smoke.png",
408 minetest.sound_play("fire_extinguish", {pos=pos,gain=0.3,pitch=math.random(80,100)/100})
415 local is_stuck = false
416 local snode = minetest.get_node_or_nil(pos)
418 local sdef = minetest.registered_nodes[snode.name] or {}
419 is_stuck = (sdef.walkable == nil or sdef.walkable == true)
420 and (sdef.collision_box == nil or sdef.collision_box.type == "regular")
421 and (sdef.node_box == nil or sdef.node_box.type == "regular")
424 -- Push item out when stuck inside solid node
428 {x=1, y=0, z=0}, {x=-1, y=0, z= 0},
429 {x=0, y=0, z=1}, {x= 0, y=0, z=-1},
432 -- Check which one of the 4 sides is free
434 local cnode = minetest.get_node(vector.add(pos, order[o])).name
435 local cdef = minetest.registered_nodes[cnode] or {}
436 if cnode ~= "ignore" and cdef.walkable == false then
441 -- If none of the 4 sides is free, check upwards
443 shootdir = {x=0, y=1, z=0}
444 local cnode = minetest.get_node(vector.add(pos, shootdir)).name
445 if cnode == "ignore" then
446 shootdir = nil -- Do not push into ignore
451 -- Set new item moving speed accordingly
452 local newv = vector.multiply(shootdir, 3)
453 self:disable_physics()
454 self.object:set_velocity(newv)
456 self.force_out = newv
457 self.force_out_start = vector.round(pos)
460 elseif self.force_out then
461 -- This code runs after the entity got a push from the above code.
462 -- It makes sure the entity is entirely outside the solid node
463 local c = self.object:get_properties().collisionbox
464 local s = self.force_out_start
465 local f = self.force_out
466 local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or
467 (f.y > 0 and pos.y + c[2] > s.y + 0.5) or
468 (f.z > 0 and pos.z + c[3] > s.z + 0.5) or
469 (f.x < 0 and pos.x + c[4] < s.x - 0.5) or
470 (f.z < 0 and pos.z + c[6] < s.z - 0.5)
472 -- Item was successfully forced out
474 self:enable_physics()
478 if not self.physical_state then
479 return -- Don't do anything
482 -- Slide on slippery nodes
483 local vel = self.object:get_velocity()
484 local def = node and minetest.registered_nodes[node.name]
485 local is_moving = (def and not def.walkable) or
486 vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
487 local is_slippery = false
489 if def and def.walkable then
490 local slippery = minetest.get_item_group(node.name, "slippery")
491 is_slippery = slippery ~= 0
492 if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
493 -- Horizontal deceleration
494 local slip_factor = 4.0 / (slippery + 4)
495 self.object:set_acceleration({
496 x = -vel.x * slip_factor,
498 z = -vel.z * slip_factor
500 elseif vel.y == 0 then
503 local collisionbox = self.object:get_properties().collisionbox
504 local move_y = collisionbox[2]
505 if self.move_up == nil then
509 if self.move_up == true then
510 move_y = move_y + (dtime/10)
512 if move_y > -0.21 then
515 elseif self.move_up == false then
516 move_y = move_y - (dtime/10)
517 if move_y < -0.5 then
521 collisionbox[2] = move_y
522 self.object:set_properties({collisionbox=collisionbox,physical = true})
527 if self.moving_state == is_moving and self.slippery_state == is_slippery then
528 -- Do not update anything until the moving state changes
532 self.moving_state = is_moving
533 self.slippery_state = is_slippery
536 self.object:set_acceleration({x = 0, y = -gravity, z = 0})
538 self.object:set_acceleration({x = 0, y = 0, z = 0})
539 self.object:set_velocity({x = 0, y = 0, z = 0})